// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "vm/object_store.h"

#include "vm/dart_entry.h"
#include "vm/exceptions.h"
#include "vm/isolate.h"
#include "vm/object.h"
#include "vm/raw_object.h"
#include "vm/resolver.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
#include "vm/visitor.h"

namespace dart {

void IsolateObjectStore::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  ASSERT(visitor != nullptr);
  visitor->set_gc_root_type("isolate_object store");
  visitor->VisitPointers(from(), to());
  visitor->clear_gc_root_type();
}

void IsolateObjectStore::Init() {
  for (ObjectPtr* current = from(); current <= to(); current++) {
    *current = Object::null();
  }
}

#ifndef PRODUCT
void IsolateObjectStore::PrintToJSONObject(JSONObject* jsobj) {
  jsobj->AddProperty("type", "_IsolateObjectStore");

  {
    JSONObject fields(jsobj, "fields");
    Object& value = Object::Handle();

    // A strtab is smaller than an array of strings.
    static const char* const names = ""
#define EMIT_FIELD_NAME(type, name) #name "_\0"
        ISOLATE_OBJECT_STORE_FIELD_LIST(EMIT_FIELD_NAME, EMIT_FIELD_NAME)
#undef EMIT_FIELD_NAME
        ;  // NOLINT
    const char* name = names;
    for (ObjectPtr* current = from(); current <= to(); current++) {
      value = *current;
      fields.AddProperty(name, value);
      name += strlen(name) + 1;
    }
  }
}
#endif  // !PRODUCT

ErrorPtr IsolateObjectStore::PreallocateObjects() {
  Thread* thread = Thread::Current();
  Isolate* isolate = thread->isolate();
  ASSERT(isolate != nullptr && isolate->isolate_object_store() == this);
  resume_capabilities_ = GrowableObjectArray::New();
  exit_listeners_ = GrowableObjectArray::New();
  error_listeners_ = GrowableObjectArray::New();
  dart_args_1_ = Array::New(1);
  dart_args_2_ = Array::New(2);
  return Error::null();
}

ObjectStore::ObjectStore()
    :
#define EMIT_FIELD_INIT(type, name) name##_(nullptr),
      OBJECT_STORE_FIELD_LIST(EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT,
                              EMIT_FIELD_INIT)
#undef EMIT_FIELD_INIT
      // Just to prevent a trailing comma.
      unused_field_(0) {
  for (ObjectPtr* current = from(); current <= to(); current++) {
    *current = Object::null();
  }
}

ObjectStore::~ObjectStore() {}

void ObjectStore::VisitObjectPointers(ObjectPointerVisitor* visitor) {
  ASSERT(visitor != nullptr);
  visitor->set_gc_root_type("object store");
  visitor->VisitPointers(from(), to());
  visitor->clear_gc_root_type();
}

#ifndef PRODUCT
void ObjectStore::PrintToJSONObject(JSONObject* jsobj) {
  jsobj->AddProperty("type", "_ObjectStore");

  {
    JSONObject fields(jsobj, "fields");
    Object& value = Object::Handle();
    // A strtab is smaller than an array of strings.
    static const char* const names = ""
#define EMIT_FIELD_NAME(type, name) #name "_\0"
        OBJECT_STORE_FIELD_LIST(
            EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME,
            EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME,
            EMIT_FIELD_NAME)
#undef EMIT_FIELD_NAME
        ;  // NOLINT
    const char* name = names;
    for (ObjectPtr* current = from(); current <= to(); current++) {
      value = *current;
      fields.AddProperty(name, value);
      name += strlen(name) + 1;
    }
  }
}
#endif  // !PRODUCT

static InstancePtr AllocateObjectByClassName(const Library& library,
                                             const String& class_name) {
  const Class& cls = Class::Handle(library.LookupClassAllowPrivate(class_name));
  ASSERT(!cls.IsNull());
  return Instance::New(cls);
}

ErrorPtr ObjectStore::PreallocateObjects() {
  Thread* thread = Thread::Current();
  IsolateGroup* isolate_group = thread->isolate_group();
  // Either we are the object store on isolate group, or isolate group has no
  // object store and we are the object store on the isolate.
  ASSERT(isolate_group != nullptr && isolate_group->object_store() == this);

  if (this->stack_overflow() != Instance::null()) {
    ASSERT(this->out_of_memory() != Instance::null());
    return Error::null();
  }
  ASSERT(this->stack_overflow() == Instance::null());
  ASSERT(this->out_of_memory() == Instance::null());

  Object& result = Object::Handle();
  const Library& library = Library::Handle(Library::CoreLibrary());

  result = AllocateObjectByClassName(library, Symbols::StackOverflowError());
  if (result.IsError()) {
    return Error::Cast(result).ptr();
  }
  set_stack_overflow(Instance::Cast(result));

  result = AllocateObjectByClassName(library, Symbols::OutOfMemoryError());
  if (result.IsError()) {
    return Error::Cast(result).ptr();
  }
  set_out_of_memory(Instance::Cast(result));

  return Error::null();
}

FunctionPtr ObjectStore::PrivateObjectLookup(const String& name) {
  const Library& core_lib = Library::Handle(core_library());
  const String& mangled = String::ZoneHandle(core_lib.PrivateName(name));
  const Class& cls = Class::Handle(object_class());
  Thread* thread = Thread::Current();
  const auto& error = cls.EnsureIsFinalized(thread);
  ASSERT(error == Error::null());
  const Function& result = Function::Handle(
      Resolver::ResolveDynamicFunction(thread->zone(), cls, mangled));
  ASSERT(!result.IsNull());
  return result.ptr();
}

void ObjectStore::InitKnownObjects() {
  Thread* thread = Thread::Current();
  Zone* zone = thread->zone();
  Class& cls = Class::Handle(zone);
  const Library& compact_hash_lib =
      Library::Handle(zone, _compact_hash_library());
  cls = compact_hash_lib.LookupClassAllowPrivate(Symbols::_Set());
  ASSERT(!cls.IsNull());
  set_set_impl_class(cls);

#if defined(DART_PRECOMPILED_RUNTIME)
  // The rest of these objects are only needed for code generation.
  return;
#else
  auto isolate_group = thread->isolate_group();
  ASSERT(isolate_group != nullptr && isolate_group->object_store() == this);

  const Library& async_lib = Library::Handle(zone, async_library());
  ASSERT(!async_lib.IsNull());
  cls = async_lib.LookupClass(Symbols::Future());
  ASSERT(!cls.IsNull());
  set_future_class(cls);

  String& function_name = String::Handle(zone);
  Function& function = Function::Handle(zone);
  Field& field = Field::Handle(zone);

  cls =
      async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController());
  ASSERT(!cls.IsNull());
  RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null());
  set_async_star_stream_controller(cls);

  function = cls.LookupFunctionAllowPrivate(Symbols::add());
  ASSERT(!function.IsNull());
  set_async_star_stream_controller_add(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::addStream());
  ASSERT(!function.IsNull());
  set_async_star_stream_controller_add_stream(function);

  field = cls.LookupFieldAllowPrivate(Symbols::asyncStarBody());
  ASSERT(!field.IsNull());
  // Force the state of guarded cid to be nullable closure so that
  // AllocateSuspendState could write to the field directly without
  // updating the guard state.
  field.set_guarded_cid(kClosureCid);
  field.set_is_nullable(true);
  set_async_star_stream_controller_async_star_body(field);

#if !defined(PRODUCT)
  // Disable debugging and inlining of all functions on the
  // _AsyncStarStreamController class.
  const Array& functions = Array::Handle(zone, cls.current_functions());
  for (intptr_t i = 0; i < functions.Length(); i++) {
    function ^= functions.At(i);
    if (function.IsNull()) {
      break;
    }
    function.set_is_debuggable(false);
    function.set_is_inlinable(false);
  }
#endif

  cls = async_lib.LookupClassAllowPrivate(Symbols::Stream());
  ASSERT(!cls.IsNull());
  set_stream_class(cls);

  cls = async_lib.LookupClassAllowPrivate(Symbols::_SuspendState());
  ASSERT(!cls.IsNull());
  const auto& error = cls.EnsureIsFinalized(thread);
  ASSERT(error == Error::null());

  function = cls.LookupFunctionAllowPrivate(Symbols::_initAsync());
  ASSERT(!function.IsNull());
  set_suspend_state_init_async(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_await());
  ASSERT(!function.IsNull());
  set_suspend_state_await(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_awaitWithTypeCheck());
  ASSERT(!function.IsNull());
  set_suspend_state_await_with_type_check(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_returnAsync());
  ASSERT(!function.IsNull());
  set_suspend_state_return_async(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_returnAsyncNotFuture());
  ASSERT(!function.IsNull());
  set_suspend_state_return_async_not_future(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_initAsyncStar());
  ASSERT(!function.IsNull());
  set_suspend_state_init_async_star(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_yieldAsyncStar());
  ASSERT(!function.IsNull());
  set_suspend_state_yield_async_star(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_returnAsyncStar());
  ASSERT(!function.IsNull());
  set_suspend_state_return_async_star(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_initSyncStar());
  ASSERT(!function.IsNull());
  set_suspend_state_init_sync_star(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_suspendSyncStarAtStart());
  ASSERT(!function.IsNull());
  set_suspend_state_suspend_sync_star_at_start(function);

  function = cls.LookupFunctionAllowPrivate(Symbols::_handleException());
  ASSERT(!function.IsNull());
  set_suspend_state_handle_exception(function);

  cls = async_lib.LookupClassAllowPrivate(Symbols::_SyncStarIterator());
  ASSERT(!cls.IsNull());
  RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null());
  set_sync_star_iterator_class(cls);

  field = cls.LookupFieldAllowPrivate(Symbols::_current());
  ASSERT(!field.IsNull());
  set_sync_star_iterator_current(field);

  field = cls.LookupFieldAllowPrivate(Symbols::_state());
  ASSERT(!field.IsNull());
  // Force the state of guarded cid to be nullable SuspendState so that
  // AllocateSuspendState could write to the field directly without
  // updating the guard state.
  field.set_guarded_cid(kSuspendStateCid);
  field.set_is_nullable(true);
  set_sync_star_iterator_state(field);

  field = cls.LookupFieldAllowPrivate(Symbols::_yieldStarIterable());
  ASSERT(!field.IsNull());
  set_sync_star_iterator_yield_star_iterable(field);

  const Library& core_lib = Library::Handle(zone, core_library());
  cls = core_lib.LookupClassAllowPrivate(Symbols::_CompileTimeError());
  ASSERT(!cls.IsNull());
  set_compiletime_error_class(cls);

  cls = core_lib.LookupClassAllowPrivate(Symbols::Pragma());
  ASSERT(!cls.IsNull());
  set_pragma_class(cls);
  RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null());
  set_pragma_name(Field::Handle(zone, cls.LookupField(Symbols::name())));
  set_pragma_options(Field::Handle(zone, cls.LookupField(Symbols::options())));

  cls = core_lib.LookupClassAllowPrivate(Symbols::_GrowableList());
  ASSERT(!cls.IsNull());
  RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null());
  growable_list_factory_ =
      cls.LookupFactoryAllowPrivate(Symbols::_GrowableListFactory());
  ASSERT(growable_list_factory_ != Function::null());

  cls = core_lib.LookupClassAllowPrivate(Symbols::Error());
  ASSERT(!cls.IsNull());
  set_error_class(cls);

  cls = core_lib.LookupClassAllowPrivate(Symbols::Expando());
  ASSERT(!cls.IsNull());
  set_expando_class(cls);

  cls = core_lib.LookupClassAllowPrivate(Symbols::Iterable());
  ASSERT(!cls.IsNull());
  set_iterable_class(cls);

  // Cache the core private functions used for fast instance of checks.
  simple_instance_of_function_ =
      PrivateObjectLookup(Symbols::_simpleInstanceOf());
  simple_instance_of_true_function_ =
      PrivateObjectLookup(Symbols::_simpleInstanceOfTrue());
  simple_instance_of_false_function_ =
      PrivateObjectLookup(Symbols::_simpleInstanceOfFalse());

  // Ensure AddSmiSmiCheckForFastSmiStubs run by the background compiler
  // will not create new functions.
  const Class& smi_class = Class::Handle(zone, this->smi_class());
  RELEASE_ASSERT(smi_class.EnsureIsFinalized(thread) == Error::null());
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::Plus());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::Minus());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::Equals());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::LAngleBracket());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::RAngleBracket());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::BitAnd());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::BitOr());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
  function_name =
      Function::CreateDynamicInvocationForwarderName(Symbols::Star());
  Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name,
                                  /*allow_add=*/true);
#endif  // defined(DART_PRECOMPILED_RUNTIME)
}

void ObjectStore::LazyInitCoreMembers() {
  auto* const thread = Thread::Current();
  SafepointWriteRwLocker locker(thread,
                                thread->isolate_group()->program_lock());
  if (list_class_.load() == Type::null()) {
    ASSERT(non_nullable_list_rare_type_.load() == Type::null());
    ASSERT(non_nullable_map_rare_type_.load() == Type::null());
    ASSERT(enum_index_field_.load() == Field::null());
    ASSERT(enum_name_field_.load() == Field::null());
    ASSERT(_object_equals_function_.load() == Function::null());
    ASSERT(_object_hash_code_function_.load() == Function::null());
    ASSERT(_object_to_string_function_.load() == Function::null());

    auto* const zone = thread->zone();
    const auto& core_lib = Library::Handle(zone, Library::CoreLibrary());
    auto& cls = Class::Handle(zone);

    cls = core_lib.LookupClass(Symbols::List());
    ASSERT(!cls.IsNull());
    list_class_.store(cls.ptr());

    auto& type = Type::Handle(zone);
    type = cls.RareType();
    non_nullable_list_rare_type_.store(type.ptr());

    cls = core_lib.LookupClass(Symbols::Map());
    ASSERT(!cls.IsNull());
    map_class_.store(cls.ptr());

    type = cls.RareType();
    non_nullable_map_rare_type_.store(type.ptr());

    cls = core_lib.LookupClass(Symbols::Set());
    ASSERT(!cls.IsNull());
    set_class_.store(cls.ptr());

    auto& field = Field::Handle(zone);

    cls = core_lib.LookupClassAllowPrivate(Symbols::_Enum());
    ASSERT(!cls.IsNull());
    const auto& error = cls.EnsureIsFinalized(thread);
    ASSERT(error == Error::null());

    field = cls.LookupInstanceField(Symbols::Index());
    ASSERT(!field.IsNull());
    enum_index_field_.store(field.ptr());

    field = cls.LookupInstanceFieldAllowPrivate(Symbols::_name());
    ASSERT(!field.IsNull());
    enum_name_field_.store(field.ptr());

    cls = core_lib.LookupClassAllowPrivate(Symbols::_BigIntImpl());
    ASSERT(!cls.IsNull());
    bigint_impl_class_.store(cls.ptr());

    auto& function = Function::Handle(zone);

    function = core_lib.LookupFunctionAllowPrivate(Symbols::_objectHashCode());
    ASSERT(!function.IsNull());
    _object_hash_code_function_.store(function.ptr());

    function = core_lib.LookupFunctionAllowPrivate(Symbols::_objectEquals());
    ASSERT(!function.IsNull());
    _object_equals_function_.store(function.ptr());

    function = core_lib.LookupFunctionAllowPrivate(Symbols::_objectToString());
    ASSERT(!function.IsNull());
    _object_to_string_function_.store(function.ptr());
  }
}

void ObjectStore::LazyInitAsyncMembers() {
  auto* const thread = Thread::Current();
  SafepointWriteRwLocker locker(thread,
                                thread->isolate_group()->program_lock());
  if (nullable_future_null_type_.load() == Type::null()) {
    ASSERT(non_nullable_future_never_type_.load() == Type::null());

    auto* const zone = thread->zone();
    const auto& cls = Class::Handle(zone, future_class());
    ASSERT(!cls.IsNull());

    auto& type_args = TypeArguments::Handle(zone);
    auto& type = Type::Handle(zone);
    type = never_type();
    ASSERT(!type.IsNull());
    type_args = TypeArguments::New(1);
    type_args.SetTypeAt(0, type);
    type = Type::New(cls, type_args, Nullability::kNonNullable);
    type.SetIsFinalized();
    type ^= type.Canonicalize(thread);
    non_nullable_future_never_type_.store(type.ptr());

    type = null_type();
    ASSERT(!type.IsNull());
    type_args = TypeArguments::New(1);
    type_args.SetTypeAt(0, type);
    type = Type::New(cls, type_args, Nullability::kNullable);
    type.SetIsFinalized();
    type ^= type.Canonicalize(thread);
    nullable_future_null_type_.store(type.ptr());
  }
}

void ObjectStore::LazyInitFfiMembers() {
  auto* const thread = Thread::Current();
  SafepointWriteRwLocker locker(thread,
                                thread->isolate_group()->program_lock());
  if (handle_finalizer_message_function_.load() == Function::null()) {
    auto* const zone = thread->zone();
    auto& cls = Class::Handle(zone);
    auto& field = Field::Handle(zone);
    auto& function = Function::Handle(zone);
    auto& error = Error::Handle(zone);

    const auto& ffi_lib = Library::Handle(zone, Library::FfiLibrary());
    ASSERT(!ffi_lib.IsNull());

    cls = finalizer_class();
    ASSERT(!cls.IsNull());
    error = cls.EnsureIsFinalized(thread);
    ASSERT(error.IsNull());
    function =
        cls.LookupFunctionAllowPrivate(Symbols::_handleFinalizerMessage());
    ASSERT(!function.IsNull());
    handle_finalizer_message_function_.store(function.ptr());

    cls = native_finalizer_class();
    ASSERT(!cls.IsNull());
    error = cls.EnsureIsFinalized(thread);
    ASSERT(error.IsNull());
    function = cls.LookupFunctionAllowPrivate(
        Symbols::_handleNativeFinalizerMessage());
    ASSERT(!function.IsNull());
    handle_native_finalizer_message_function_.store(function.ptr());

    cls = ffi_lib.LookupClass(Symbols::FfiNative());
    ASSERT(!cls.IsNull());
    error = cls.EnsureIsFinalized(thread);
    ASSERT(error.IsNull());
    function =
        cls.LookupStaticFunctionAllowPrivate(Symbols::_ffi_resolver_function());
    ASSERT(!function.IsNull());
    ffi_resolver_function_.store(function.ptr());

    cls = ffi_lib.LookupClass(Symbols::Array());
    ASSERT(!cls.IsNull());
    ffi_array_class_.store(cls.ptr());

    cls = ffi_lib.LookupClassAllowPrivate(Symbols::_Compound());
    ASSERT(!cls.IsNull());
    error = cls.EnsureIsFinalized(thread);
    ASSERT(error.IsNull());
    ffi_compound_class_.store(cls.ptr());

    field = cls.LookupInstanceFieldAllowPrivate(Symbols::_offsetInBytes());
    ASSERT(!field.IsNull());
    compound_offset_in_bytes_field_.store(field.ptr());

    field = cls.LookupInstanceFieldAllowPrivate(Symbols::_typedDataBase());
    ASSERT(!field.IsNull());
    compound_typed_data_base_field_.store(field.ptr());

    cls = ffi_lib.LookupClass(Symbols::Struct());
    ASSERT(!cls.IsNull());
    ffi_struct_class_.store(cls.ptr());

    cls = ffi_lib.LookupClass(Symbols::Union());
    ASSERT(!cls.IsNull());
    ffi_union_class_.store(cls.ptr());

    cls = ffi_lib.LookupClass(Symbols::VarArgs());
    ASSERT(!cls.IsNull());
    ffi_varargs_class_.store(cls.ptr());
  }
}

void ObjectStore::LazyInitIsolateMembers() {
  auto* const thread = Thread::Current();
  SafepointWriteRwLocker locker(thread,
                                thread->isolate_group()->program_lock());
  if (lookup_port_handler_.load() == Type::null()) {
    ASSERT(lookup_open_ports_.load() == Type::null());
    ASSERT(handle_message_function_.load() == Type::null());

    auto* const zone = thread->zone();
    const auto& isolate_lib = Library::Handle(zone, Library::IsolateLibrary());
    auto& cls = Class::Handle(zone);
    auto& function = Function::Handle(zone);

    cls = isolate_lib.LookupClass(Symbols::Capability());
    ASSERT(!cls.IsNull());
    capability_class_.store(cls.ptr());

    cls = isolate_lib.LookupClass(Symbols::SendPort());
    ASSERT(!cls.IsNull());
    send_port_class_.store(cls.ptr());

    cls = isolate_lib.LookupClass(Symbols::TransferableTypedData());
    ASSERT(!cls.IsNull());
    transferable_class_.store(cls.ptr());

    cls = isolate_lib.LookupClassAllowPrivate(Symbols::_RawReceivePort());
    ASSERT(!cls.IsNull());
    const auto& error = cls.EnsureIsFinalized(thread);
    ASSERT(error == Error::null());

    function = cls.LookupFunctionAllowPrivate(Symbols::_lookupHandler());
    ASSERT(!function.IsNull());
    lookup_port_handler_.store(function.ptr());

    function = cls.LookupFunctionAllowPrivate(Symbols::_lookupOpenPorts());
    ASSERT(!function.IsNull());
    lookup_open_ports_.store(function.ptr());

    function = cls.LookupFunctionAllowPrivate(Symbols::_handleMessage());
    ASSERT(!function.IsNull());
    handle_message_function_.store(function.ptr());
  }
}

void ObjectStore::LazyInitInternalMembers() {
  auto* const thread = Thread::Current();
  SafepointWriteRwLocker locker(thread,
                                thread->isolate_group()->program_lock());
  if (symbol_class_.load() == Type::null()) {
    ASSERT(symbol_name_field_.load() == Field::null());

    auto* const zone = thread->zone();
    auto& cls = Class::Handle(zone);
    auto& field = Field::Handle(zone);
    auto& error = Error::Handle(zone);

    const auto& internal_lib =
        Library::Handle(zone, Library::InternalLibrary());
    cls = internal_lib.LookupClass(Symbols::Symbol());
    ASSERT(!cls.IsNull());
    error = cls.EnsureIsFinalized(thread);
    ASSERT(error.IsNull());
    symbol_class_.store(cls.ptr());

    field = cls.LookupInstanceFieldAllowPrivate(Symbols::_name());
    ASSERT(!field.IsNull());
    symbol_name_field_.store(field.ptr());
  }
}

}  // namespace dart
