/* -*- 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/. */ // Copyright 2019 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "irregexp/RegExpShim.h" #include "mozilla/MemoryReporting.h" #include #include "irregexp/imported/regexp-macro-assembler.h" #include "irregexp/imported/regexp-stack.h" #include "vm/NativeObject-inl.h" namespace v8 { namespace internal { void PrintF(const char* format, ...) { va_list arguments; va_start(arguments, format); vprintf(format, arguments); va_end(arguments); } void PrintF(FILE* out, const char* format, ...) { va_list arguments; va_start(arguments, format); vfprintf(out, format, arguments); va_end(arguments); } StdoutStream::operator std::ostream&() const { return std::cerr; } template std::ostream& StdoutStream::operator<<(T t) { return std::cerr << t; } template std::ostream& StdoutStream::operator<<(char const* c); // Origin: // https://github.com/v8/v8/blob/855591a54d160303349a5f0a32fab15825c708d1/src/utils/ostreams.cc#L120-L169 // (This is a hand-simplified version.) // Writes the given character to the output escaping everything outside // of printable ASCII range. std::ostream& operator<<(std::ostream& os, const AsUC16& c) { base::uc16 v = c.value; bool isPrint = 0x20 < v && v <= 0x7e; char buf[10]; const char* format = isPrint ? "%c" : (v <= 0xFF) ? "\\x%02x" : "\\u%04x"; SprintfLiteral(buf, format, v); return os << buf; } std::ostream& operator<<(std::ostream& os, const AsUC32& c) { int32_t v = c.value; if (v <= String::kMaxUtf16CodeUnit) { return os << AsUC16(v); } char buf[13]; SprintfLiteral(buf, "\\u{%06x}", v); return os << buf; } HandleScope::HandleScope(Isolate* isolate) : isolate_(isolate) { isolate->openHandleScope(*this); } HandleScope::~HandleScope() { isolate_->closeHandleScope(level_, non_gc_level_); } template Handle::Handle(T object, Isolate* isolate) : location_(isolate->getHandleLocation(object.value())) {} template Handle::Handle(ByteArray b, Isolate* isolate); template Handle::Handle(const JS::Value& v, Isolate* isolate); template Handle::Handle(JSRegExp re, Isolate* isolate); template Handle::Handle(String s, Isolate* isolate); template Handle::Handle(const JS::Value& value, Isolate* isolate) : location_(isolate->getHandleLocation(value)) { T::cast(Object(value)); // Assert that value has the correct type. } JS::Value* Isolate::getHandleLocation(const JS::Value& value) { js::AutoEnterOOMUnsafeRegion oomUnsafe; if (!handleArena_.Append(value)) { oomUnsafe.crash("Irregexp handle allocation"); } return &handleArena_.GetLast(); } void* Isolate::allocatePseudoHandle(size_t bytes) { PseudoHandle ptr; ptr.reset(js_malloc(bytes)); if (!ptr) { return nullptr; } if (!uniquePtrArena_.Append(std::move(ptr))) { return nullptr; } return uniquePtrArena_.GetLast().get(); } template PseudoHandle Isolate::takeOwnership(void* ptr) { PseudoHandle result = maybeTakeOwnership(ptr); MOZ_ASSERT(result); return result; } template PseudoHandle Isolate::maybeTakeOwnership(void* ptr) { for (auto iter = uniquePtrArena_.IterFromLast(); !iter.Done(); iter.Prev()) { auto& entry = iter.Get(); if (entry.get() == ptr) { PseudoHandle result; result.reset(static_cast(entry.release())); return result; } } return PseudoHandle(); } PseudoHandle ByteArray::maybeTakeOwnership(Isolate* isolate) { PseudoHandle result = isolate->maybeTakeOwnership(value().toPrivate()); setValue(JS::PrivateValue(nullptr)); return result; } PseudoHandle ByteArray::takeOwnership(Isolate* isolate) { PseudoHandle result = maybeTakeOwnership(isolate); MOZ_ASSERT(result); return result; } void Isolate::trace(JSTracer* trc) { js::gc::AssertRootMarkingPhase(trc); for (auto iter = handleArena_.Iter(); !iter.Done(); iter.Next()) { auto& elem = iter.Get(); JS::GCPolicy::trace(trc, &elem, "Isolate handle arena"); } } size_t Isolate::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { size_t size = mallocSizeOf(this); size += mallocSizeOf(regexpStack_); size += ExternalReference::SizeOfExcludingThis(mallocSizeOf, regexpStack_); size += handleArena_.SizeOfExcludingThis(mallocSizeOf); size += uniquePtrArena_.SizeOfExcludingThis(mallocSizeOf); return size; } /*static*/ Handle String::Flatten(Isolate* isolate, Handle string) { if (string->IsFlat()) { return string; } js::AutoEnterOOMUnsafeRegion oomUnsafe; JSLinearString* linear = string->str()->ensureLinear(isolate->cx()); if (!linear) { oomUnsafe.crash("Irregexp String::Flatten"); } return Handle(JS::StringValue(linear), isolate); } // This is only used for trace messages printing the source pattern of // a regular expression. We have to return a unique_ptr, but we don't // care about the contents, so we return an empty null-terminated string. std::unique_ptr String::ToCString() { js::AutoEnterOOMUnsafeRegion oomUnsafe; std::unique_ptr ptr; ptr.reset(static_cast(js_malloc(1))); if (!ptr) { oomUnsafe.crash("Irregexp String::ToCString"); } ptr[0] = '\0'; return ptr; } bool Isolate::init() { regexpStack_ = js_new(); if (!regexpStack_) { return false; } return true; } Isolate::~Isolate() { if (regexpStack_) { js_delete(regexpStack_); } } /* static */ const void* ExternalReference::TopOfRegexpStack(Isolate* isolate) { return reinterpret_cast( isolate->regexp_stack()->memory_top_address_address()); } /* static */ size_t ExternalReference::SizeOfExcludingThis( mozilla::MallocSizeOf mallocSizeOf, RegExpStack* regexpStack) { if (regexpStack->thread_local_.owns_memory_) { return mallocSizeOf(regexpStack->thread_local_.memory_); } return 0; } Handle Isolate::NewByteArray(int length, AllocationType alloc) { MOZ_RELEASE_ASSERT(length >= 0); js::AutoEnterOOMUnsafeRegion oomUnsafe; size_t alloc_size = sizeof(uint32_t) + length; ByteArrayData* data = static_cast(allocatePseudoHandle(alloc_size)); if (!data) { oomUnsafe.crash("Irregexp NewByteArray"); } data->length = length; return Handle(JS::PrivateValue(data), this); } Handle Isolate::NewFixedArray(int length) { MOZ_RELEASE_ASSERT(length >= 0); js::AutoEnterOOMUnsafeRegion oomUnsafe; js::ArrayObject* array = js::NewDenseFullyAllocatedArray(cx(), length); if (!array) { oomUnsafe.crash("Irregexp NewFixedArray"); } array->ensureDenseInitializedLength(0, length); return Handle(JS::ObjectValue(*array), this); } template Handle> Isolate::NewFixedIntegerArray(uint32_t length) { MOZ_RELEASE_ASSERT(length < std::numeric_limits::max() / sizeof(T)); js::AutoEnterOOMUnsafeRegion oomUnsafe; uint32_t rawLength = length * sizeof(T); size_t allocSize = sizeof(ByteArrayData) + rawLength; ByteArrayData* data = static_cast(allocatePseudoHandle(allocSize)); if (!data) { oomUnsafe.crash("Irregexp NewFixedIntegerArray"); } data->length = rawLength; return Handle>(JS::PrivateValue(data), this); } template Handle> FixedIntegerArray::New(Isolate* isolate, uint32_t length) { return isolate->NewFixedIntegerArray(length); } template class FixedIntegerArray; template Handle Isolate::InternalizeString( const base::Vector& str) { js::AutoEnterOOMUnsafeRegion oomUnsafe; JSAtom* atom = js::AtomizeChars(cx(), str.begin(), str.length()); if (!atom) { oomUnsafe.crash("Irregexp InternalizeString"); } return Handle(JS::StringValue(atom), this); } template Handle Isolate::InternalizeString( const base::Vector& str); template Handle Isolate::InternalizeString( const base::Vector& str); static_assert(JSRegExp::RegistersForCaptureCount(JSRegExp::kMaxCaptures) <= RegExpMacroAssembler::kMaxRegisterCount); } // namespace internal } // namespace v8