Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/frontend/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 41 kB image not shown  

Quelle  ParserAtom.cpp   Sprache: C

 
/* -*- 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 "frontend/ParserAtom.h"

#include "mozilla/TextUtils.h"  // mozilla::IsAscii

#include <memory>  // std::uninitialized_fill_n

#include "jsnum.h"  // CharsToNumber

#include "frontend/CompilationStencil.h"
#include "js/GCAPI.h"            // JS::AutoSuppressGCAnalysis
#include "js/Printer.h"          // Sprinter, QuoteString
#include "util/Identifier.h"     // IsIdentifier
#include "util/StringBuilder.h"  // StringBuilder
#include "util/Text.h"           // AsciiDigitToNumber
#include "util/Unicode.h"
#include "vm/JSContext.h"
#include "vm/Runtime.h"
#include "vm/SelfHosting.h"  // ExtendedUnclonedSelfHostedFunctionNamePrefix
#include "vm/StaticStrings.h"
#include "vm/StringType.h"

using namespace js;
using namespace js::frontend;

namespace js {
namespace frontend {

JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId atomId) {
#define ASSERT_OFFSET_(NAME, _)                  \
  static_assert(offsetof(JSAtomState, NAME) ==   \
                int32_t(WellKnownAtomId::NAME) * \
                    sizeof(js::ImmutableTenuredPtr<PropertyName*>));
  FOR_EACH_COMMON_PROPERTYNAME(ASSERT_OFFSET_);
#undef ASSERT_OFFSET_

#define ASSERT_OFFSET_(NAME, _)                  \
  static_assert(offsetof(JSAtomState, NAME) ==   \
                int32_t(WellKnownAtomId::NAME) * \
                    sizeof(js::ImmutableTenuredPtr<PropertyName*>));
  JS_FOR_EACH_PROTOTYPE(ASSERT_OFFSET_);
#undef ASSERT_OFFSET_

#define ASSERT_OFFSET_(NAME)                     \
  static_assert(offsetof(JSAtomState, NAME) ==   \
                int32_t(WellKnownAtomId::NAME) * \
                    sizeof(js::ImmutableTenuredPtr<PropertyName*>));
  JS_FOR_EACH_WELL_KNOWN_SYMBOL(ASSERT_OFFSET_);
#undef ASSERT_OFFSET_

  static_assert(int32_t(WellKnownAtomId::abort) == 0,
                "Unexpected order of WellKnownAtom");

  return (&cx->names().abort)[int32_t(atomId)];
}

#ifdef DEBUG
void TaggedParserAtomIndex::validateRaw() {
  if (isParserAtomIndex()) {
    MOZ_ASSERT(toParserAtomIndex().index < IndexLimit);
  } else if (isWellKnownAtomId()) {
    MOZ_ASSERT(uint32_t(toWellKnownAtomId()) <
               uint32_t(WellKnownAtomId::Limit));
  } else if (isLength1StaticParserString()) {
    // always valid
  } else if (isLength2StaticParserString()) {
    MOZ_ASSERT(size_t(toLength2StaticParserString()) < Length2StaticLimit);
  } else if (isLength3StaticParserString()) {
    // always valid
  } else {
    MOZ_ASSERT(isNull());
  }
}
#endif

HashNumber TaggedParserAtomIndex::staticOrWellKnownHash() const {
  MOZ_ASSERT(!isParserAtomIndex());

  if (isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(toWellKnownAtomId());
    return info.hash;
  }

  if (isLength1StaticParserString()) {
    Latin1Char content[1];
    ParserAtomsTable::getLength1Content(toLength1StaticParserString(), content);
    return mozilla::HashString(content, 1);
  }

  if (isLength2StaticParserString()) {
    char content[2];
    ParserAtomsTable::getLength2Content(toLength2StaticParserString(), content);
    return mozilla::HashString(reinterpret_cast<const Latin1Char*>(content), 2);
  }

  MOZ_ASSERT(isLength3StaticParserString());
  char content[3];
  ParserAtomsTable::getLength3Content(toLength3StaticParserString(), content);
  return mozilla::HashString(reinterpret_cast<const Latin1Char*>(content), 3);
}

template <typename CharT, typename SeqCharT>
/* static */ ParserAtom* ParserAtom::allocate(
    FrontendContext* fc, LifoAlloc& alloc, InflatedChar16Sequence<SeqCharT> seq,
    uint32_t length, HashNumber hash) {
  constexpr size_t HeaderSize = sizeof(ParserAtom);
  void* raw = alloc.alloc(HeaderSize + (sizeof(CharT) * length));
  if (!raw) {
    js::ReportOutOfMemory(fc);
    return nullptr;
  }

  constexpr bool hasTwoByteChars = (sizeof(CharT) == 2);
  static_assert(sizeof(CharT) == 1 || sizeof(CharT) == 2,
                "CharT should be 1 or 2 byte type");
  ParserAtom* entry = new (raw) ParserAtom(length, hash, hasTwoByteChars);
  CharT* entryBuf = entry->chars<CharT>();
  drainChar16Seq(entryBuf, seq, length);
  return entry;
}

bool ParserAtom::isInstantiatedAsJSAtom() const {
  if (isMarkedAtomize()) {
    return true;
  }

  // Always use JSAtom for short strings.
  if (length() < MinimumLengthForNonAtom) {
    return true;
  }

  return false;
}

JSString* ParserAtom::instantiateString(JSContext* cx, FrontendContext* fc,
                                        ParserAtomIndex index,
                                        CompilationAtomCache& atomCache) const {
  MOZ_ASSERT(!isInstantiatedAsJSAtom());

  JSString* str;
  if (hasLatin1Chars()) {
    str = NewStringCopyNDontDeflateNonStaticValidLength<CanGC>(
        cx, latin1Chars(), length(), gc::Heap::Tenured);
  } else {
    str = NewStringCopyNDontDeflateNonStaticValidLength<CanGC>(
        cx, twoByteChars(), length(), gc::Heap::Tenured);
  }
  if (!str) {
    return nullptr;
  }
  if (!atomCache.setAtomAt(fc, index, str)) {
    return nullptr;
  }

  return str;
}

JSAtom* ParserAtom::instantiateAtom(JSContext* cx, FrontendContext* fc,
                                    ParserAtomIndex index,
                                    CompilationAtomCache& atomCache) const {
  MOZ_ASSERT(isInstantiatedAsJSAtom());

  JSAtom* atom;
  if (hasLatin1Chars()) {
    atom =
        AtomizeCharsNonStaticValidLength(cx, hash(), latin1Chars(), length());
  } else {
    atom =
        AtomizeCharsNonStaticValidLength(cx, hash(), twoByteChars(), length());
  }
  if (!atom) {
    return nullptr;
  }
  if (!atomCache.setAtomAt(fc, index, atom)) {
    return nullptr;
  }
  return atom;
}

JSAtom* ParserAtom::instantiatePermanentAtom(
    JSContext* cx, FrontendContext* fc, AtomSet& atomSet, ParserAtomIndex index,
    CompilationAtomCache& atomCache) const {
  MOZ_ASSERT(!cx->zone());

  MOZ_ASSERT(hasLatin1Chars());
  MOZ_ASSERT(length() <= JSString::MAX_LENGTH);
  JSAtom* atom = PermanentlyAtomizeCharsNonStaticValidLength(
      cx, atomSet, hash(), latin1Chars(), length());
  if (!atom) {
    return nullptr;
  }
  if (!atomCache.setAtomAt(fc, index, atom)) {
    return nullptr;
  }
  return atom;
}

#if defined(DEBUG) || defined(JS_JITSPEW)
void ParserAtom::dump() const {
  js::Fprinter out(stderr);
  out.put("\"");
  dumpCharsNoQuote(out);
  out.put("\"\n");
}

void ParserAtom::dumpCharsNoQuote(js::GenericPrinter& out) const {
  if (hasLatin1Chars()) {
    JSString::dumpCharsNoQuote<Latin1Char>(latin1Chars(), length(), out);
  } else {
    JSString::dumpCharsNoQuote<char16_t>(twoByteChars(), length(), out);
  }
}

void ParserAtomsTable::dump(TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    getParserAtom(index.toParserAtomIndex())->dump();
    return;
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    js::Fprinter out(stderr);
    out.put("\"");
    out.put(info.content, info.length);
    out.put("\"");
    return;
  }

  if (index.isLength1StaticParserString()) {
    js::Fprinter out(stderr);
    out.put("\"");
    dumpCharsNoQuote(out, index.toLength1StaticParserString());
    out.put("\"\n");
    return;
  }

  if (index.isLength2StaticParserString()) {
    js::Fprinter out(stderr);
    out.put("\"");
    dumpCharsNoQuote(out, index.toLength2StaticParserString());
    out.put("\"\n");
    return;
  }

  if (index.isLength3StaticParserString()) {
    js::Fprinter out(stderr);
    out.put("\"");
    dumpCharsNoQuote(out, index.toLength3StaticParserString());
    out.put("\"\n");
    return;
  }

  MOZ_ASSERT(index.isNull());
  js::Fprinter out(stderr);
  out.put("#");
}

void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
                                        TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    getParserAtom(index.toParserAtomIndex())->dumpCharsNoQuote(out);
    return;
  }

  if (index.isWellKnownAtomId()) {
    dumpCharsNoQuote(out, index.toWellKnownAtomId());
    return;
  }

  if (index.isLength1StaticParserString()) {
    dumpCharsNoQuote(out, index.toLength1StaticParserString());
    return;
  }

  if (index.isLength2StaticParserString()) {
    dumpCharsNoQuote(out, index.toLength2StaticParserString());
    return;
  }

  if (index.isLength3StaticParserString()) {
    dumpCharsNoQuote(out, index.toLength3StaticParserString());
    return;
  }

  MOZ_ASSERT(index.isNull());
  out.put("#");
}

/* static */
void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
                                        WellKnownAtomId id) {
  const auto& info = GetWellKnownAtomInfo(id);
  out.put(info.content, info.length);
}

/* static */
void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
                                        Length1StaticParserString index) {
  Latin1Char content[1];
  getLength1Content(index, content);
  out.putChar(content[0]);
}

/* static */
void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
                                        Length2StaticParserString index) {
  char content[2];
  getLength2Content(index, content);
  out.putChar(content[0]);
  out.putChar(content[1]);
}

/* static */
void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out,
                                        Length3StaticParserString index) {
  char content[3];
  getLength3Content(index, content);
  out.putChar(content[0]);
  out.putChar(content[1]);
  out.putChar(content[2]);
}
#endif

ParserAtomsTable::ParserAtomsTable(LifoAlloc& alloc) : alloc_(&alloc) {}

TaggedParserAtomIndex ParserAtomsTable::addEntry(FrontendContext* fc,
                                                 EntryMap::AddPtr& addPtr,
                                                 ParserAtom* entry) {
  MOZ_ASSERT(!addPtr);
  ParserAtomIndex index = ParserAtomIndex(entries_.length());
  if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) {
    ReportAllocationOverflow(fc);
    return TaggedParserAtomIndex::null();
  }
  if (!entries_.append(entry)) {
    js::ReportOutOfMemory(fc);
    return TaggedParserAtomIndex::null();
  }
  auto taggedIndex = TaggedParserAtomIndex(index);
  if (!entryMap_.add(addPtr, entry, taggedIndex)) {
    js::ReportOutOfMemory(fc);
    return TaggedParserAtomIndex::null();
  }
  return taggedIndex;
}

template <typename AtomCharT, typename SeqCharT>
TaggedParserAtomIndex ParserAtomsTable::internChar16Seq(
    FrontendContext* fc, EntryMap::AddPtr& addPtr, HashNumber hash,
    InflatedChar16Sequence<SeqCharT> seq, uint32_t length) {
  MOZ_ASSERT(!addPtr);

  ParserAtom* entry =
      ParserAtom::allocate<AtomCharT>(fc, *alloc_, seq, length, hash);
  if (!entry) {
    return TaggedParserAtomIndex::null();
  }
  return addEntry(fc, addPtr, entry);
}

static const uint16_t MAX_LATIN1_CHAR = 0xff;

TaggedParserAtomIndex ParserAtomsTable::internAscii(FrontendContext* fc,
                                                    const char* asciiPtr,
                                                    uint32_t length) {
  // ASCII strings are strict subsets of Latin1 strings.
  const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(asciiPtr);
  return internLatin1(fc, latin1Ptr, length);
}

TaggedParserAtomIndex ParserAtomsTable::internLatin1(
    FrontendContext* fc, const Latin1Char* latin1Ptr, uint32_t length) {
  // Check for tiny strings which are abundant in minified code.
  if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndex(
          latin1Ptr, length)) {
    return tiny;
  }

  // Check for well-known atom.
  InflatedChar16Sequence<Latin1Char> seq(latin1Ptr, length);
  SpecificParserAtomLookup<Latin1Char> lookup(seq);
  if (auto wk = WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)) {
    return wk;
  }

  // Check for existing atom.
  auto addPtr = entryMap_.lookupForAdd(lookup);
  if (addPtr) {
    return addPtr->value();
  }

  return internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq, length);
}

bool IsWide(const InflatedChar16Sequence<char16_t>& seq) {
  InflatedChar16Sequence<char16_t> seqCopy = seq;
  while (seqCopy.hasMore()) {
    char16_t ch = seqCopy.next();
    if (ch > MAX_LATIN1_CHAR) {
      return true;
    }
  }

  return false;
}

template <typename AtomCharT>
TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtomImpl(
    FrontendContext* fc, const ParserAtom* atom) {
  InflatedChar16Sequence<AtomCharT> seq(atom->chars<AtomCharT>(),
                                        atom->length());
  SpecificParserAtomLookup<AtomCharT> lookup(seq, atom->hash());

  // Check for existing atom.
  auto addPtr = entryMap_.lookupForAdd(lookup);
  if (addPtr) {
    auto index = addPtr->value();

    // Copy UsedByStencilFlag and AtomizeFlag.
    MOZ_ASSERT(entries_[index.toParserAtomIndex()]->hasTwoByteChars() ==
               atom->hasTwoByteChars());
    entries_[index.toParserAtomIndex()]->flags_ |= atom->flags_;
    return index;
  }

  auto index =
      internChar16Seq<AtomCharT>(fc, addPtr, atom->hash(), seq, atom->length());
  if (!index) {
    return TaggedParserAtomIndex::null();
  }

  // Copy UsedByStencilFlag and AtomizeFlag.
  MOZ_ASSERT(entries_[index.toParserAtomIndex()]->hasTwoByteChars() ==
             atom->hasTwoByteChars());
  entries_[index.toParserAtomIndex()]->flags_ |= atom->flags_;
  return index;
}

TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtom(
    FrontendContext* fc, const ParserAtom* atom) {
  if (atom->hasLatin1Chars()) {
    return internExternalParserAtomImpl<JS::Latin1Char>(fc, atom);
  }
  return internExternalParserAtomImpl<char16_t>(fc, atom);
}

bool ParserAtomsTable::addPlaceholder(FrontendContext* fc) {
  ParserAtomIndex index = ParserAtomIndex(entries_.length());
  if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) {
    ReportAllocationOverflow(fc);
    return false;
  }
  if (!entries_.append(nullptr)) {
    js::ReportOutOfMemory(fc);
    return false;
  }
  return true;
}

TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtomIndex(
    FrontendContext* fc, const CompilationStencil& context,
    TaggedParserAtomIndex atom) {
  // When the atom is not a parser atom index, the value represent the atom
  // without the need for a ParserAtom, and thus we can skip interning it.
  if (!atom.isParserAtomIndex()) {
    return atom;
  }
  auto index = atom.toParserAtomIndex();
  return internExternalParserAtom(fc, context.parserAtomData[index]);
}

bool ParserAtomsTable::isEqualToExternalParserAtomIndex(
    TaggedParserAtomIndex internal, const CompilationStencil& context,
    TaggedParserAtomIndex external) const {
  // If one is null, well-known or static, then testing the equality of the bits
  // of the TaggedParserAtomIndex is sufficient.
  if (!internal.isParserAtomIndex() || !external.isParserAtomIndex()) {
    return internal == external;
  }

  // Otherwise we have to compare 2 atom-indexes from different ParserAtomTable.
  ParserAtom* internalAtom = getParserAtom(internal.toParserAtomIndex());
  ParserAtom* externalAtom =
      context.parserAtomData[external.toParserAtomIndex()];

  if (internalAtom->hash() != externalAtom->hash()) {
    return false;
  }

  HashNumber hash = internalAtom->hash();
  size_t length = internalAtom->length();
  if (internalAtom->hasLatin1Chars()) {
    const Latin1Char* chars = internalAtom->latin1Chars();
    InflatedChar16Sequence<Latin1Char> seq(chars, length);
    return externalAtom->equalsSeq(hash, seq);
  }

  const char16_t* chars = internalAtom->twoByteChars();
  InflatedChar16Sequence<char16_t> seq(chars, length);
  return externalAtom->equalsSeq(hash, seq);
}

bool ParserAtomSpanBuilder::allocate(FrontendContext* fc, LifoAlloc& alloc,
                                     size_t count) {
  if (count >= TaggedParserAtomIndex::IndexLimit) {
    ReportAllocationOverflow(fc);
    return false;
  }

  auto* p = alloc.newArrayUninitialized<ParserAtom*>(count);
  if (!p) {
    js::ReportOutOfMemory(fc);
    return false;
  }
  std::uninitialized_fill_n(p, count, nullptr);

  entries_ = mozilla::Span(p, count);
  return true;
}

static inline bool IsLatin1(mozilla::Utf8Unit c1, mozilla::Utf8Unit c2) {
  auto u1 = c1.toUint8();
  auto u2 = c2.toUint8();

  // 0x80-0xBF
  if (u1 == 0xC2 && 0x80 <= u2 && u2 <= 0xBF) {
    return true;
  }

  // 0xC0-0xFF
  if (u1 == 0xC3 && 0x80 <= u2 && u2 <= 0xBF) {
    return true;
  }

  return false;
}

TaggedParserAtomIndex ParserAtomsTable::internUtf8(
    FrontendContext* fc, const mozilla::Utf8Unit* utf8Ptr, uint32_t nbyte) {
  if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndexUTF8(
          utf8Ptr, nbyte)) {
    return tiny;
  }

  // If source text is ASCII, then the length of the target char buffer
  // is the same as the length of the UTF8 input.  Convert it to a Latin1
  // encoded string on the heap.
  JS::UTF8Chars utf8(utf8Ptr, nbyte);
  JS::SmallestEncoding minEncoding = FindSmallestEncoding(utf8);
  if (minEncoding == JS::SmallestEncoding::ASCII) {
    // As ascii strings are a subset of Latin1 strings, and each encoding
    // unit is the same size, we can reliably cast this `Utf8Unit*`
    // to a `Latin1Char*`.
    const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(utf8Ptr);
    return internLatin1(fc, latin1Ptr, nbyte);
  }

  // Check for existing.
  // NOTE: Well-known are all ASCII so have been handled above.
  InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, nbyte);
  SpecificParserAtomLookup<mozilla::Utf8Unit> lookup(seq);
  MOZ_ASSERT(!WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup));
  EntryMap::AddPtr addPtr = entryMap_.lookupForAdd(lookup);
  if (addPtr) {
    return addPtr->value();
  }

  // Compute length in code-points.
  uint32_t length = 0;
  InflatedChar16Sequence<mozilla::Utf8Unit> seqCopy = seq;
  while (seqCopy.hasMore()) {
    (void)seqCopy.next();
    length += 1;
  }

  // Otherwise, add new entry.
  bool wide = (minEncoding == JS::SmallestEncoding::UTF16);
  return wide
             ? internChar16Seq<char16_t>(fc, addPtr, lookup.hash(), seq, length)
             : internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq,
                                           length);
}

TaggedParserAtomIndex ParserAtomsTable::internChar16(FrontendContext* fc,
                                                     const char16_t* char16Ptr,
                                                     uint32_t length) {
  // Check for tiny strings which are abundant in minified code.
  if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndex(
          char16Ptr, length)) {
    return tiny;
  }

  // Check against well-known.
  InflatedChar16Sequence<char16_t> seq(char16Ptr, length);
  SpecificParserAtomLookup<char16_t> lookup(seq);
  if (auto wk = WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)) {
    return wk;
  }

  // Check for existing atom.
  EntryMap::AddPtr addPtr = entryMap_.lookupForAdd(lookup);
  if (addPtr) {
    return addPtr->value();
  }

  // Otherwise, add new entry.
  return IsWide(seq)
             ? internChar16Seq<char16_t>(fc, addPtr, lookup.hash(), seq, length)
             : internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq,
                                           length);
}

TaggedParserAtomIndex ParserAtomsTable::internJSAtom(
    FrontendContext* fc, CompilationAtomCache& atomCache, JSAtom* atom) {
  TaggedParserAtomIndex parserAtom;
  {
    JS::AutoCheckCannotGC nogc;

    parserAtom =
        atom->hasLatin1Chars()
            ? internLatin1(fc, atom->latin1Chars(nogc), atom->length())
            : internChar16(fc, atom->twoByteChars(nogc), atom->length());
    if (!parserAtom) {
      return TaggedParserAtomIndex::null();
    }
  }

  if (parserAtom.isParserAtomIndex()) {
    ParserAtomIndex index = parserAtom.toParserAtomIndex();
    if (!atomCache.hasAtomAt(index)) {
      if (!atomCache.setAtomAt(fc, index, atom)) {
        return TaggedParserAtomIndex::null();
      }
    }
  }

  // We should (infallibly) map back to the same JSAtom.
#ifdef DEBUG
  if (JSContext* cx = fc->maybeCurrentJSContext()) {
    JS::AutoSuppressGCAnalysis suppress(cx);
    MOZ_ASSERT(toJSAtom(cx, fc, parserAtom, atomCache) == atom);
  }
#endif

  return parserAtom;
}

ParserAtom* ParserAtomsTable::getParserAtom(ParserAtomIndex index) const {
  return entries_[index];
}

void ParserAtomsTable::markUsedByStencil(TaggedParserAtomIndex index,
                                         ParserAtom::Atomize atomize) const {
  if (!index.isParserAtomIndex()) {
    return;
  }

  getParserAtom(index.toParserAtomIndex())->markUsedByStencil(atomize);
}

void ParserAtomsTable::markAtomize(TaggedParserAtomIndex index,
                                   ParserAtom::Atomize atomize) const {
  if (!index.isParserAtomIndex()) {
    return;
  }

  getParserAtom(index.toParserAtomIndex())->markAtomize(atomize);
}

bool ParserAtomsTable::isIdentifier(TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    return atom->hasLatin1Chars()
               ? IsIdentifier(atom->latin1Chars(), atom->length())
               : IsIdentifier(atom->twoByteChars(), atom->length());
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return IsIdentifier(reinterpret_cast<const Latin1Char*>(info.content),
                        info.length);
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    if (MOZ_UNLIKELY(content[0] > 127)) {
      return IsIdentifier(content, 1);
    }
    return IsIdentifierASCII(char(content[0]));
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    return IsIdentifierASCII(content[0], content[1]);
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
#ifdef DEBUG
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  MOZ_ASSERT(!reinterpret_cast<const Latin1Char*>(
      IsIdentifier(reinterpret_cast<const Latin1Char*>(content), 3)));
#endif
  return false;
}

bool ParserAtomsTable::isPrivateName(TaggedParserAtomIndex index) const {
  if (!index.isParserAtomIndex()) {
    return false;
  }

  const auto* atom = getParserAtom(index.toParserAtomIndex());
  return atom->isPrivateName();
}

bool ParserAtomsTable::isExtendedUnclonedSelfHostedFunctionName(
    TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    if (atom->length() < 2) {
      return false;
    }

    return atom->charAt(0) == ExtendedUnclonedSelfHostedFunctionNamePrefix;
  }

  if (index.isWellKnownAtomId()) {
    switch (index.toWellKnownAtomId()) {
      case WellKnownAtomId::dollar_ArrayBufferSpecies_:
      case WellKnownAtomId::dollar_ArraySpecies_:
      case WellKnownAtomId::dollar_ArrayValues_:
      case WellKnownAtomId::dollar_RegExpFlagsGetter_:
      case WellKnownAtomId::dollar_RegExpToString_: {
#ifdef DEBUG
        const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
        MOZ_ASSERT(info.content[0] ==
                   ExtendedUnclonedSelfHostedFunctionNamePrefix);
#endif
        return true;
      }
      default: {
#ifdef DEBUG
        const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
        MOZ_ASSERT(info.length == 0 ||
                   info.content[0] !=
                       ExtendedUnclonedSelfHostedFunctionNamePrefix);
#endif
        break;
      }
    }
    return false;
  }

  // Length-1/2/3 shouldn't be used for extented uncloned self-hosted
  // function name, and this query shouldn't be used for them.
#ifdef DEBUG
  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix);
  } else if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix);
  } else {
    MOZ_ASSERT(index.isLength3StaticParserString());
    char content[3];
    getLength3Content(index.toLength3StaticParserString(), content);
    MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix);
  }
#endif
  return false;
}

static bool HasUnpairedSurrogate(mozilla::Range<const char16_t> chars) {
  for (auto ptr = chars.begin(); ptr < chars.end();) {
    char16_t ch = *ptr++;
    if (unicode::IsLeadSurrogate(ch)) {
      if (ptr == chars.end() || !unicode::IsTrailSurrogate(*ptr++)) {
        return true;
      }
    } else if (unicode::IsTrailSurrogate(ch)) {
      return true;
    }
  }
  return false;
}

bool ParserAtomsTable::isModuleExportName(TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const ParserAtom* name = getParserAtom(index.toParserAtomIndex());
    return name->hasLatin1Chars() ||
           !HasUnpairedSurrogate(name->twoByteRange());
  }

  // Well-known/length-2 are ASCII.
  // length-1 are Latin1.
  return true;
}

bool ParserAtomsTable::isIndex(TaggedParserAtomIndex index,
                               uint32_t* indexp) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    size_t len = atom->length();
    if (len == 0 || len > UINT32_CHAR_BUFFER_LENGTH) {
      return false;
    }
    if (atom->hasLatin1Chars()) {
      return mozilla::IsAsciiDigit(*atom->latin1Chars()) &&
             js::CheckStringIsIndex(atom->latin1Chars(), len, indexp);
    }
    return mozilla::IsAsciiDigit(*atom->twoByteChars()) &&
           js::CheckStringIsIndex(atom->twoByteChars(), len, indexp);
  }

  if (index.isWellKnownAtomId()) {
#ifdef DEBUG
    // Well-known atom shouldn't start with digit.
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    MOZ_ASSERT(info.length == 0 || !mozilla::IsAsciiDigit(info.content[0]));
#endif
    return false;
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    if (mozilla::IsAsciiDigit(content[0])) {
      *indexp = AsciiDigitToNumber(content[0]);
      return true;
    }
    return false;
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    // Leading '0' isn't allowed.
    // See CheckStringIsIndex comment.
    if (content[0] != '0' && mozilla::IsAsciiDigit(content[0]) &&
        mozilla::IsAsciiDigit(content[1])) {
      *indexp =
          AsciiDigitToNumber(content[0]) * 10 + AsciiDigitToNumber(content[1]);
      return true;
    }
    return false;
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  *indexp = uint32_t(index.toLength3StaticParserString());
#ifdef DEBUG
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  MOZ_ASSERT(uint32_t(AsciiDigitToNumber(content[0])) * 100 +
                 uint32_t(AsciiDigitToNumber(content[1])) * 10 +
                 uint32_t(AsciiDigitToNumber(content[2])) ==
             *indexp);
  MOZ_ASSERT(100 <= *indexp);
#endif
  return true;
}

bool ParserAtomsTable::isInstantiatedAsJSAtom(
    TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    return atom->isInstantiatedAsJSAtom();
  }

  // Everything else are always JSAtom.
  return true;
}

uint32_t ParserAtomsTable::length(TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    return getParserAtom(index.toParserAtomIndex())->length();
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return info.length;
  }

  if (index.isLength1StaticParserString()) {
    return 1;
  }

  if (index.isLength2StaticParserString()) {
    return 2;
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  return 3;
}

HashNumber ParserAtomsTable::hash(TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    return getParserAtom(index.toParserAtomIndex())->hash();
  }

  return index.staticOrWellKnownHash();
}

double ParserAtomsTable::toNumber(TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    size_t len = atom->length();
    return atom->hasLatin1Chars() ? CharsToNumber(atom->latin1Chars(), len)
                                  : CharsToNumber(atom->twoByteChars(), len);
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return CharsToNumber(reinterpret_cast<const Latin1Char*>(info.content),
                         info.length);
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    return CharsToNumber(content, 1);
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    return CharsToNumber(reinterpret_cast<const Latin1Char*>(content), 2);
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  double result = double(index.toLength3StaticParserString());
#ifdef DEBUG
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  double tmp = CharsToNumber(reinterpret_cast<const Latin1Char*>(content), 3);
  MOZ_ASSERT(tmp == result);
#endif
  return result;
}

UniqueChars ParserAtomsTable::toNewUTF8CharsZ(
    FrontendContext* fc, TaggedParserAtomIndex index) const {
  auto* alloc = fc->getAllocator();

  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    return UniqueChars(
        atom->hasLatin1Chars()
            ? JS::CharsToNewUTF8CharsZ(alloc, atom->latin1Range()).c_str()
            : JS::CharsToNewUTF8CharsZ(alloc, atom->twoByteRange()).c_str());
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return UniqueChars(
        JS::CharsToNewUTF8CharsZ(
            alloc,
            mozilla::Range(reinterpret_cast<const Latin1Char*>(info.content),
                           info.length))
            .c_str());
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    return UniqueChars(
        JS::CharsToNewUTF8CharsZ(alloc, mozilla::Range(content, 1)).c_str());
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    return UniqueChars(
        JS::CharsToNewUTF8CharsZ(
            alloc,
            mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2))
            .c_str());
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  return UniqueChars(
      JS::CharsToNewUTF8CharsZ(
          alloc,
          mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3))
          .c_str());
}

template <typename CharT>
UniqueChars ToPrintableStringImpl(mozilla::Range<CharT> str,
                                  char quote = '\0') {
  // Pass nullptr as JSContext, given we don't use JSString.
  // OOM should be handled by caller.
  Sprinter sprinter(nullptr);
  if (!sprinter.init()) {
    return nullptr;
  }
  QuoteString<QuoteTarget::String>(&sprinter, str, quote);
  return sprinter.release();
}

UniqueChars ParserAtomsTable::toPrintableString(
    TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    return atom->hasLatin1Chars() ? ToPrintableStringImpl(atom->latin1Range())
                                  : ToPrintableStringImpl(atom->twoByteRange());
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return ToPrintableStringImpl(mozilla::Range(
        reinterpret_cast<const Latin1Char*>(info.content), info.length));
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    return ToPrintableStringImpl(mozilla::Range<const Latin1Char>(content, 1));
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    return ToPrintableStringImpl(
        mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2));
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  return ToPrintableStringImpl(
      mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3));
}

UniqueChars ParserAtomsTable::toQuotedString(
    TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    return atom->hasLatin1Chars()
               ? ToPrintableStringImpl(atom->latin1Range(), '\"')
               : ToPrintableStringImpl(atom->twoByteRange(), '\"');
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return ToPrintableStringImpl(
        mozilla::Range(reinterpret_cast<const Latin1Char*>(info.content),
                       info.length),
        '\"');
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    return ToPrintableStringImpl(mozilla::Range<const Latin1Char>(content, 1),
                                 '\"');
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    return ToPrintableStringImpl(
        mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2), '\"');
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  return ToPrintableStringImpl(
      mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3), '\"');
}

JSAtom* ParserAtomsTable::toJSAtom(JSContext* cx, FrontendContext* fc,
                                   TaggedParserAtomIndex index,
                                   CompilationAtomCache& atomCache) const {
  // This function can be called before we instantiate atoms based on
  // AtomizeFlag.

  if (index.isParserAtomIndex()) {
    auto atomIndex = index.toParserAtomIndex();

    // If we already instantiated this parser atom, it should always be JSAtom.
    // `asAtom()` called in getAtomAt asserts that.
    JSAtom* atom = atomCache.getAtomAt(atomIndex);
    if (atom) {
      return atom;
    }

    // For consistency, mark atomize.
    ParserAtom* parserAtom = getParserAtom(atomIndex);
    parserAtom->markAtomize(ParserAtom::Atomize::Yes);
    return parserAtom->instantiateAtom(cx, fc, atomIndex, atomCache);
  }

  if (index.isWellKnownAtomId()) {
    return GetWellKnownAtom(cx, index.toWellKnownAtomId());
  }

  if (index.isLength1StaticParserString()) {
    char16_t ch = static_cast<char16_t>(index.toLength1StaticParserString());
    return cx->staticStrings().getUnit(ch);
  }

  if (index.isLength2StaticParserString()) {
    size_t s = static_cast<size_t>(index.toLength2StaticParserString());
    return cx->staticStrings().getLength2FromIndex(s);
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  uint32_t s = uint32_t(index.toLength3StaticParserString());
  return cx->staticStrings().getUint(s);
}

bool ParserAtomsTable::appendTo(StringBuilder& sb,
                                TaggedParserAtomIndex index) const {
  if (index.isParserAtomIndex()) {
    const auto* atom = getParserAtom(index.toParserAtomIndex());
    size_t length = atom->length();
    return atom->hasLatin1Chars() ? sb.append(atom->latin1Chars(), length)
                                  : sb.append(atom->twoByteChars(), length);
  }

  if (index.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId());
    return sb.append(info.content, info.length);
  }

  if (index.isLength1StaticParserString()) {
    Latin1Char content[1];
    getLength1Content(index.toLength1StaticParserString(), content);
    return sb.append(content[0]);
  }

  if (index.isLength2StaticParserString()) {
    char content[2];
    getLength2Content(index.toLength2StaticParserString(), content);
    return sb.append(content, 2);
  }

  MOZ_ASSERT(index.isLength3StaticParserString());
  char content[3];
  getLength3Content(index.toLength3StaticParserString(), content);
  return sb.append(content, 3);
}

bool InstantiateMarkedAtoms(JSContext* cx, FrontendContext* fc,
                            const ParserAtomSpan& entries,
                            CompilationAtomCache& atomCache) {
  MOZ_ASSERT(cx->zone());

  for (size_t i = 0; i < entries.size(); i++) {
    const auto& entry = entries[i];
    if (!entry) {
      continue;
    }
    if (!entry->isUsedByStencil()) {
      continue;
    }

    auto index = ParserAtomIndex(i);
    if (atomCache.hasAtomAt(index)) {
      continue;
    }

    if (!entry->isInstantiatedAsJSAtom()) {
      if (!entry->instantiateString(cx, fc, index, atomCache)) {
        return false;
      }
    } else {
      if (!entry->instantiateAtom(cx, fc, index, atomCache)) {
        return false;
      }
    }
  }
  return true;
}

bool InstantiateMarkedAtomsAsPermanent(JSContext* cx, FrontendContext* fc,
                                       AtomSet& atomSet,
                                       const ParserAtomSpan& entries,
                                       CompilationAtomCache& atomCache) {
  MOZ_ASSERT(!cx->zone());

  for (size_t i = 0; i < entries.size(); i++) {
    const auto& entry = entries[i];
    if (!entry) {
      continue;
    }
    if (!entry->isUsedByStencil()) {
      continue;
    }

    auto index = ParserAtomIndex(i);
    if (atomCache.hasAtomAt(index)) {
      MOZ_ASSERT(atomCache.getAtomAt(index)->isPermanentAtom());
      continue;
    }

    if (!entry->instantiatePermanentAtom(cx, fc, atomSet, index, atomCache)) {
      return false;
    }
  }
  return true;
}

/* static */
MOZ_RUNINIT WellKnownParserAtoms WellKnownParserAtoms::singleton_;

template <typename CharT>
TaggedParserAtomIndex WellKnownParserAtoms::lookupChar16Seq(
    const SpecificParserAtomLookup<CharT>& lookup) const {
  EntryMap::Ptr ptr = wellKnownMap_.readonlyThreadsafeLookup(lookup);
  if (ptr) {
    return ptr->value();
  }
  return TaggedParserAtomIndex::null();
}

TaggedParserAtomIndex WellKnownParserAtoms::lookupTinyIndexUTF8(
    const mozilla::Utf8Unit* utf8Ptr, size_t nbyte) const {
  // Check for tiny strings which are abundant in minified code.
  if (nbyte == 2 && IsLatin1(utf8Ptr[0], utf8Ptr[1])) {
    // Special case the length-1 non-ASCII range.
    InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, 2);
    char16_t u = seq.next();
    const Latin1Char c = u;
    MOZ_ASSERT(!seq.hasMore());
    auto tiny = lookupTinyIndex(&c, 1);
    MOZ_ASSERT(tiny);
    return tiny;
  }

  // NOTE: Other than length-1 non-ASCII range, the tiny atoms are all
  //       ASCII-only so we can directly look at the UTF-8 data without
  //       worrying about surrogates.
  return lookupTinyIndex(reinterpret_cast<const Latin1Char*>(utf8Ptr), nbyte);
}

bool WellKnownParserAtoms::initSingle(const WellKnownAtomInfo& info,
                                      TaggedParserAtomIndex index) {
  unsigned int len = info.length;
  const Latin1Char* str = reinterpret_cast<const Latin1Char*>(info.content);

  // Well-known atoms are all currently ASCII with length <= MaxWellKnownLength.
  MOZ_ASSERT(len <= MaxWellKnownLength);
  MOZ_ASSERT(mozilla::IsAscii(mozilla::Span(info.content, len)));

  // Strings matched by lookupTinyIndex are stored in static table and aliases
  // should be initialized directly in WellKnownParserAtoms::init.
  MOZ_ASSERT(lookupTinyIndex(str, len) == TaggedParserAtomIndex::null(),
             "Well-known atom matches a tiny StaticString. Did you add it to "
             "the wrong CommonPropertyNames.h list?");

  InflatedChar16Sequence<Latin1Char> seq(str, len);
  SpecificParserAtomLookup<Latin1Char> lookup(seq, info.hash);

  // Save name for returning after moving entry into set.
  if (!wellKnownMap_.putNew(lookup, &info, index)) {
    return false;
  }

  return true;
}

bool WellKnownParserAtoms::init() {
  MOZ_ASSERT(wellKnownMap_.empty());

  // Add well-known strings to the HashMap. The HashMap is used for dynamic
  // lookups later and does not change once this init method is complete.
#define COMMON_NAME_INIT_(NAME, _)                             \
  if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \
                  TaggedParserAtomIndex::WellKnown::NAME())) { \
    return false;                                              \
  }
  FOR_EACH_NONTINY_COMMON_PROPERTYNAME(COMMON_NAME_INIT_)
#undef COMMON_NAME_INIT_
#define COMMON_NAME_INIT_(NAME, _)                             \
  if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \
                  TaggedParserAtomIndex::WellKnown::NAME())) { \
    return false;                                              \
  }
  JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INIT_)
#undef COMMON_NAME_INIT_
#define COMMON_NAME_INIT_(NAME)                                \
  if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \
                  TaggedParserAtomIndex::WellKnown::NAME())) { \
    return false;                                              \
  }
  JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INIT_)
#undef COMMON_NAME_INIT_

  return true;
}

void WellKnownParserAtoms::free() { wellKnownMap_.clear(); }

/* static */ bool WellKnownParserAtoms::initSingleton() {
  return singleton_.init();
}

/* static */ void WellKnownParserAtoms::freeSingleton() { singleton_.free(); }

/* namespace frontend */
/* namespace js */

Messung V0.5
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.