blob: 003a02d301061dfc4c9a6b442e399f89ac409854 [file] [log] [blame]
// Copyright 2017 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 "src/objects/template-objects.h"
#include "src/base/functional.h"
#include "src/execution/isolate.h"
#include "src/heap/factory.h"
#include "src/objects/js-array.h"
#include "src/objects/objects-inl.h"
#include "src/objects/property-descriptor.h"
#include "src/objects/template-objects-inl.h"
namespace v8 {
namespace internal {
namespace {
bool CachedTemplateMatches(Isolate* isolate,
Tagged<NativeContext> native_context,
Tagged<JSArray> entry, int function_literal_id,
int slot_id, DisallowGarbageCollection& no_gc) {
if (native_context->is_js_array_template_literal_object_map(
entry->map(isolate))) {
Tagged<TemplateLiteralObject> template_object =
TemplateLiteralObject::cast(entry);
return template_object->function_literal_id() == function_literal_id &&
template_object->slot_id() == slot_id;
}
Handle<JSArray> entry_handle(entry, isolate);
Tagged<Smi> cached_function_literal_id =
Smi::cast(*JSReceiver::GetDataProperty(
isolate, entry_handle,
isolate->factory()->template_literal_function_literal_id_symbol()));
if (cached_function_literal_id.value() != function_literal_id) return false;
Tagged<Smi> cached_slot_id = Smi::cast(*JSReceiver::GetDataProperty(
isolate, entry_handle,
isolate->factory()->template_literal_slot_id_symbol()));
if (cached_slot_id.value() != slot_id) return false;
return true;
}
} // namespace
// static
Handle<JSArray> TemplateObjectDescription::GetTemplateObject(
Isolate* isolate, Handle<NativeContext> native_context,
Handle<TemplateObjectDescription> description,
Handle<SharedFunctionInfo> shared_info, int slot_id) {
int function_literal_id = shared_info->function_literal_id();
// Check the template weakmap to see if the template object already exists.
Handle<Script> script(Script::cast(shared_info->script(isolate)), isolate);
int32_t hash =
EphemeronHashTable::TodoShape::Hash(ReadOnlyRoots(isolate), script);
MaybeHandle<ArrayList> maybe_cached_templates;
if (!IsUndefined(native_context->template_weakmap(), isolate)) {
DisallowGarbageCollection no_gc;
// The no_gc keeps this safe, and gcmole is confused because
// CachedTemplateMatches calls JSReceiver::GetDataProperty.
DisableGCMole no_gcmole;
ReadOnlyRoots roots(isolate);
Tagged<EphemeronHashTable> template_weakmap =
EphemeronHashTable::cast(native_context->template_weakmap());
Tagged<Object> cached_templates_lookup =
template_weakmap->Lookup(isolate, script, hash);
if (!IsTheHole(cached_templates_lookup, roots)) {
Tagged<ArrayList> cached_templates =
ArrayList::cast(cached_templates_lookup);
maybe_cached_templates = handle(cached_templates, isolate);
// Linear search over the cached template array list for a template
// object matching the given function_literal_id + slot_id.
// TODO(leszeks): Consider keeping this list sorted for faster lookup.
for (int i = 0; i < cached_templates->length(); i++) {
Tagged<JSArray> template_object =
JSArray::cast(cached_templates->get(i));
if (CachedTemplateMatches(isolate, *native_context, template_object,
function_literal_id, slot_id, no_gc)) {
return handle(template_object, isolate);
}
}
}
}
// Create the raw object from the {raw_strings}.
Handle<FixedArray> raw_strings(description->raw_strings(), isolate);
Handle<FixedArray> cooked_strings(description->cooked_strings(), isolate);
Handle<JSArray> template_object =
isolate->factory()->NewJSArrayForTemplateLiteralArray(
cooked_strings, raw_strings, function_literal_id, slot_id);
// Insert the template object into the cached template array list.
Handle<ArrayList> cached_templates;
if (!maybe_cached_templates.ToHandle(&cached_templates)) {
cached_templates = isolate->factory()->NewArrayList(1);
}
cached_templates = ArrayList::Add(isolate, cached_templates, template_object);
// Compare the cached_templates to the original maybe_cached_templates loaded
// from the weakmap -- if it doesn't match, we need to update the weakmap.
Handle<ArrayList> old_cached_templates;
if (!maybe_cached_templates.ToHandle(&old_cached_templates) ||
*old_cached_templates != *cached_templates) {
Tagged<HeapObject> maybe_template_weakmap =
native_context->template_weakmap();
Handle<EphemeronHashTable> template_weakmap;
if (IsUndefined(maybe_template_weakmap)) {
template_weakmap = EphemeronHashTable::New(isolate, 1);
} else {
template_weakmap =
handle(EphemeronHashTable::cast(maybe_template_weakmap), isolate);
}
template_weakmap = EphemeronHashTable::Put(isolate, template_weakmap,
script, cached_templates, hash);
native_context->set_template_weakmap(*template_weakmap);
}
// Check that the list is in the appropriate location on the weakmap, and
// that the appropriate entry is in the right location in this list.
DCHECK_EQ(EphemeronHashTable::cast(native_context->template_weakmap())
->Lookup(isolate, script, hash),
*cached_templates);
DCHECK_EQ(cached_templates->get(cached_templates->length() - 1),
*template_object);
return template_object;
}
} // namespace internal
} // namespace v8