[esnext] implement spec change to TaggedTemplate callsite caching

Implements the change outlined in https://github.com/tc39/ecma262/pull/890,
which has been ratified and pulled into the specification. In particular,
template callsite objects are no longer kept in a global, eternal Map, but
are instead associated with their callsite, which can be collected. This
prevents a memory leak incurred by TaggedTemplate calls.

Changes, summarized:

    - Remove the TemplateMap and TemplateMapShape objects, instead caching
      template objects in the feedback vector.
    - Remove the `hash` member of TemplateObjectDescriptor, and the Equals
      method (used by TemplateMap)
    - Add a new FeedbackSlotKind (kTemplateObject), which behaves similarly
      to FeedbackSlotKind::kLiteral, but prevents eval caching. This ensures
      that a new feedback vector is always created for eval() containing tagged
      templates, even when the CompilationCache is used.
    - GetTemplateObject bytecode now takes a feedback index, and only calls
      into the runtime if the feedback is Smi::kZero (uninitialized).

BUG=v8:3230, v8:2891
R=littledan@chromium.org, yangguo@chromium.org, bmeurer@chromium.org,
rmcilroy@chromium.org

Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I7827bc148d3d93e2b056ebf63dd624da196ad423
Reviewed-on: https://chromium-review.googlesource.com/624564
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51248}
This commit is contained in:
Caitlin Potter 2018-02-12 11:27:02 -05:00 committed by Commit Bot
parent ae0447fa48
commit d3ca0d0050
28 changed files with 148 additions and 256 deletions

View File

@ -683,8 +683,8 @@ Handle<TemplateObjectDescription> GetTemplateObject::GetOrBuildDescription(
}
}
}
return isolate->factory()->NewTemplateObjectDescription(
this->hash(), raw_strings, cooked_strings);
return isolate->factory()->NewTemplateObjectDescription(raw_strings,
cooked_strings);
}
static bool IsCommutativeOperationWithSmiLiteral(Token::Value op) {

View File

@ -2633,7 +2633,6 @@ class GetTemplateObject final : public Expression {
const ZoneList<const AstRawString*>* raw_strings() const {
return raw_strings_;
}
int hash() const { return hash_; }
Handle<TemplateObjectDescription> GetOrBuildDescription(Isolate* isolate);
@ -2641,16 +2640,13 @@ class GetTemplateObject final : public Expression {
friend class AstNodeFactory;
GetTemplateObject(const ZoneList<const AstRawString*>* cooked_strings,
const ZoneList<const AstRawString*>* raw_strings, int hash,
int pos)
const ZoneList<const AstRawString*>* raw_strings, int pos)
: Expression(pos, kGetTemplateObject),
cooked_strings_(cooked_strings),
raw_strings_(raw_strings),
hash_(hash) {}
raw_strings_(raw_strings) {}
const ZoneList<const AstRawString*>* cooked_strings_;
const ZoneList<const AstRawString*>* raw_strings_;
int hash_;
};
// ----------------------------------------------------------------------------
@ -3225,9 +3221,8 @@ class AstNodeFactory final BASE_EMBEDDED {
GetTemplateObject* NewGetTemplateObject(
const ZoneList<const AstRawString*>* cooked_strings,
const ZoneList<const AstRawString*>* raw_strings, int hash, int pos) {
return new (zone_)
GetTemplateObject(cooked_strings, raw_strings, hash, pos);
const ZoneList<const AstRawString*>* raw_strings, int pos) {
return new (zone_) GetTemplateObject(cooked_strings, raw_strings, pos);
}
ImportCallExpression* NewImportCallExpression(Expression* args, int pos) {

View File

@ -316,6 +316,9 @@ void CompilationCache::PutEval(Handle<String> source,
Handle<Cell> literals, int position) {
if (!IsEnabled()) return;
// TemplateObject feedback slots disable eval caching
if (function_info->feedback_metadata()->HasTemplateObjectSlot()) return;
HandleScope scope(isolate());
if (context->IsNativeContext()) {
eval_global_.Put(source, outer_info, function_info, context, literals,

View File

@ -1584,12 +1584,21 @@ void BytecodeGraphBuilder::VisitGetTemplateObject() {
Handle<TemplateObjectDescription> description =
Handle<TemplateObjectDescription>::cast(
bytecode_iterator().GetConstantForIndexOperand(0));
// It's not observable when the template object is created, so we
// can just create it eagerly during graph building and bake in
// the JSArray constant here.
Node* template_object =
jsgraph()->HeapConstant(TemplateObjectDescription::GetTemplateObject(
description, native_context()));
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
FeedbackNexus nexus(feedback_vector(), slot);
Handle<JSArray> cached_value;
if (nexus.GetFeedback() == Smi::kZero) {
// It's not observable when the template object is created, so we
// can just create it eagerly during graph building and bake in
// the JSArray constant here.
cached_value = TemplateObjectDescription::CreateTemplateObject(description);
nexus.vector()->Set(slot, *cached_value);
} else {
cached_value = handle(JSArray::cast(nexus.GetFeedback()));
}
Node* template_object = jsgraph()->HeapConstant(cached_value);
environment()->BindAccumulator(template_object);
}

View File

@ -406,7 +406,6 @@ enum ContextLookupFlags {
V(WASM_MEMORY_CONSTRUCTOR_INDEX, JSFunction, wasm_memory_constructor) \
V(WASM_MODULE_CONSTRUCTOR_INDEX, JSFunction, wasm_module_constructor) \
V(WASM_TABLE_CONSTRUCTOR_INDEX, JSFunction, wasm_table_constructor) \
V(TEMPLATE_MAP_INDEX, HeapObject, template_map) \
V(TYPED_ARRAY_FUN_INDEX, JSFunction, typed_array_function) \
V(TYPED_ARRAY_PROTOTYPE_INDEX, JSObject, typed_array_prototype) \
V(UINT16_ARRAY_FUN_INDEX, JSFunction, uint16_array_fun) \

View File

@ -149,13 +149,11 @@ Handle<ConstantElementsPair> Factory::NewConstantElementsPair(
}
Handle<TemplateObjectDescription> Factory::NewTemplateObjectDescription(
int hash, Handle<FixedArray> raw_strings,
Handle<FixedArray> cooked_strings) {
Handle<FixedArray> raw_strings, Handle<FixedArray> cooked_strings) {
DCHECK_EQ(raw_strings->length(), cooked_strings->length());
DCHECK_LT(0, raw_strings->length());
Handle<TemplateObjectDescription> result =
Handle<TemplateObjectDescription>::cast(NewStruct(TUPLE3_TYPE, TENURED));
result->set_hash(hash);
Handle<TemplateObjectDescription>::cast(NewStruct(TUPLE2_TYPE, TENURED));
result->set_raw_strings(*raw_strings);
result->set_cooked_strings(*cooked_strings);
return result;

View File

@ -165,8 +165,7 @@ class V8_EXPORT_PRIVATE Factory final {
// Create a new TemplateObjectDescription struct.
Handle<TemplateObjectDescription> NewTemplateObjectDescription(
int hash, Handle<FixedArray> raw_strings,
Handle<FixedArray> cooked_strings);
Handle<FixedArray> raw_strings, Handle<FixedArray> cooked_strings);
// Create a pre-tenured empty AccessorPair.
Handle<AccessorPair> NewAccessorPair();

View File

@ -35,6 +35,16 @@ int FeedbackMetadata::slot_count() const {
return Smi::ToInt(get(kSlotsCountIndex));
}
bool FeedbackMetadata::HasTemplateObjectSlot() const {
DisallowHeapAllocation no_gc;
FeedbackMetadataIterator iter(const_cast<FeedbackMetadata*>(this));
while (iter.HasNext()) {
iter.Next();
if (iter.kind() == FeedbackSlotKind::kTemplateObject) return true;
}
return false;
}
// static
FeedbackVector* FeedbackVector::cast(Object* obj) {
DCHECK(obj->IsFeedbackVector());
@ -48,6 +58,7 @@ int FeedbackMetadata::GetSlotSize(FeedbackSlotKind kind) {
case FeedbackSlotKind::kCompareOp:
case FeedbackSlotKind::kBinaryOp:
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kTypeProfile:
return 1;
@ -307,6 +318,7 @@ void FeedbackVector::ComputeCounts(int* with_type_info, int* generic,
}
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
break;
case FeedbackSlotKind::kInvalid:
case FeedbackSlotKind::kKindsNumber:

View File

@ -173,6 +173,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
return "kCreateClosure";
case FeedbackSlotKind::kLiteral:
return "Literal";
case FeedbackSlotKind::kTemplateObject:
return "TemplateObject";
case FeedbackSlotKind::kTypeProfile:
return "TypeProfile";
case FeedbackSlotKind::kForIn:
@ -255,6 +257,7 @@ Handle<FeedbackVector> FeedbackVector::New(Isolate* isolate,
break;
}
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
vector->set(index, Smi::kZero, SKIP_WRITE_BARRIER);
break;
case FeedbackSlotKind::kCall:
@ -415,6 +418,7 @@ void FeedbackNexus::ConfigureUninitialized() {
switch (kind()) {
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
break;
case FeedbackSlotKind::kStoreGlobalSloppy:
@ -479,6 +483,7 @@ bool FeedbackNexus::Clear() {
break;
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
SetFeedback(Smi::kZero, SKIP_WRITE_BARRIER);
feedback_updated = true;
break;
@ -549,6 +554,7 @@ InlineCacheState FeedbackNexus::StateFromFeedback() const {
switch (kind()) {
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
// CreateClosure and literal slots don't have a notion of state.
UNREACHABLE();
break;

View File

@ -48,6 +48,7 @@ enum class FeedbackSlotKind {
kTypeProfile,
kCreateClosure,
kLiteral,
kTemplateObject,
kForIn,
kInstanceOf,
@ -379,6 +380,9 @@ class V8_EXPORT_PRIVATE FeedbackVectorSpec {
}
FeedbackSlot AddLiteralSlot() { return AddSlot(FeedbackSlotKind::kLiteral); }
FeedbackSlot AddTemplateObjectSlot() {
return AddSlot(FeedbackSlotKind::kTemplateObject);
}
FeedbackSlot AddStoreDataPropertyInLiteralICSlot() {
return AddSlot(FeedbackSlotKind::kStoreDataPropertyInLiteral);
@ -427,6 +431,8 @@ class FeedbackMetadata : public FixedArray {
// Returns number of slots in the vector.
inline int slot_count() const;
inline bool HasTemplateObjectSlot() const;
// Returns slot kind for given slot.
FeedbackSlotKind GetKind(FeedbackSlot slot) const;

View File

@ -973,8 +973,8 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEmptyObjectLiteral() {
}
BytecodeArrayBuilder& BytecodeArrayBuilder::GetTemplateObject(
size_t template_object_description_entry) {
OutputGetTemplateObject(template_object_description_entry);
size_t template_object_description_entry, int feedback_slot) {
OutputGetTemplateObject(template_object_description_entry, feedback_slot);
return *this;
}

View File

@ -240,7 +240,7 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
// Gets or creates the template for a TemplateObjectDescription which will
// be inserted at constant pool index |template_object_description_entry|.
BytecodeArrayBuilder& GetTemplateObject(
size_t template_object_description_entry);
size_t template_object_description_entry, int feedback_slot);
// Push the context in accumulator as the new context, and store in register
// |context|.

View File

@ -4174,7 +4174,8 @@ void BytecodeGenerator::VisitGetTemplateObject(GetTemplateObject* expr) {
builder()->SetExpressionPosition(expr);
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
template_objects_.push_back(std::make_pair(expr, entry));
builder()->GetTemplateObject(entry);
FeedbackSlot literal_slot = feedback_spec()->AddTemplateObjectSlot();
builder()->GetTemplateObject(entry, feedback_index(literal_slot));
}
void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) {

View File

@ -233,7 +233,8 @@ namespace interpreter {
V(CreateEmptyObjectLiteral, AccumulatorUse::kWrite) \
\
/* Tagged templates */ \
V(GetTemplateObject, AccumulatorUse::kWrite, OperandType::kIdx) \
V(GetTemplateObject, AccumulatorUse::kWrite, OperandType::kIdx, \
OperandType::kIdx) \
\
/* Closure allocation */ \
V(CreateClosure, AccumulatorUse::kWrite, OperandType::kIdx, \

View File

@ -2417,18 +2417,34 @@ IGNITION_HANDLER(CreateEmptyObjectLiteral, InterpreterAssembler) {
Dispatch();
}
// GetTemplateObject
// GetTemplateObject <descriptor_idx> <literal_idx>
//
// Creates the template to pass for tagged templates and returns it in the
// accumulator, creating and caching the site object on-demand as per the
// specification.
IGNITION_HANDLER(GetTemplateObject, InterpreterAssembler) {
Node* description = LoadConstantPoolEntryAtOperandIndex(0);
Node* context = GetContext();
Node* feedback_vector = LoadFeedbackVector();
Node* slot = BytecodeOperandIdx(1);
Node* cached_value =
LoadFeedbackVectorSlot(feedback_vector, slot, 0, INTPTR_PARAMETERS);
Node* result = CallRuntime(Runtime::kGetTemplateObject, context, description);
SetAccumulator(result);
Label call_runtime(this, Label::kDeferred);
GotoIf(WordEqual(cached_value, SmiConstant(0)), &call_runtime);
SetAccumulator(cached_value);
Dispatch();
BIND(&call_runtime);
{
Node* description = LoadConstantPoolEntryAtOperandIndex(0);
Node* context = GetContext();
Node* result =
CallRuntime(Runtime::kCreateTemplateObject, context, description);
StoreFeedbackVectorSlot(feedback_vector, slot, result, UPDATE_WRITE_BARRIER,
0, INTPTR_PARAMETERS);
SetAccumulator(result);
Dispatch();
}
}
// CreateClosure <index> <slot> <tenured>

View File

@ -112,8 +112,7 @@ TYPE_CHECKER(PropertyDescriptorObject, FIXED_ARRAY_TYPE)
TYPE_CHECKER(SmallOrderedHashMap, SMALL_ORDERED_HASH_MAP_TYPE)
TYPE_CHECKER(SmallOrderedHashSet, SMALL_ORDERED_HASH_SET_TYPE)
TYPE_CHECKER(SourcePositionTableWithFrameCache, TUPLE2_TYPE)
TYPE_CHECKER(TemplateMap, HASH_TABLE_TYPE)
TYPE_CHECKER(TemplateObjectDescription, TUPLE3_TYPE)
TYPE_CHECKER(TemplateObjectDescription, TUPLE2_TYPE)
TYPE_CHECKER(TransitionArray, TRANSITION_ARRAY_TYPE)
TYPE_CHECKER(WasmInstanceObject, WASM_INSTANCE_TYPE)
TYPE_CHECKER(WasmMemoryObject, WASM_MEMORY_TYPE)
@ -624,7 +623,6 @@ CAST_ACCESSOR(StringSet)
CAST_ACCESSOR(StringTable)
CAST_ACCESSOR(Struct)
CAST_ACCESSOR(TemplateInfo)
CAST_ACCESSOR(TemplateMap)
CAST_ACCESSOR(TemplateObjectDescription)
CAST_ACCESSOR(Tuple2)
CAST_ACCESSOR(Tuple3)
@ -2365,7 +2363,6 @@ bool ConstantElementsPair::is_empty() const {
return constant_values()->length() == 0;
}
SMI_ACCESSORS(TemplateObjectDescription, hash, kHashOffset)
ACCESSORS(TemplateObjectDescription, raw_strings, FixedArray, kRawStringsOffset)
ACCESSORS(TemplateObjectDescription, cooked_strings, FixedArray,
kCookedStringsOffset)

View File

@ -843,6 +843,7 @@ void FeedbackNexus::Print(std::ostream& os) { // NOLINT
}
case FeedbackSlotKind::kCreateClosure:
case FeedbackSlotKind::kLiteral:
case FeedbackSlotKind::kTemplateObject:
case FeedbackSlotKind::kTypeProfile:
break;
case FeedbackSlotKind::kInvalid:

View File

@ -16455,8 +16455,6 @@ template class HashTable<ObjectHashTable, ObjectHashTableShape>;
template class HashTable<WeakHashTable, WeakHashTableShape>;
template class HashTable<TemplateMap, TemplateMapShape>;
template class Dictionary<NameDictionary, NameDictionaryShape>;
template class Dictionary<GlobalDictionary, GlobalDictionaryShape>;

View File

@ -958,7 +958,6 @@ class FeedbackVector;
class WeakCell;
class TransitionArray;
class TemplateList;
class TemplateMap;
template <typename T>
class ZoneForwardList;
@ -1114,7 +1113,6 @@ template <class C> inline bool Is(Object* obj);
V(Symbol) \
V(TemplateInfo) \
V(TemplateList) \
V(TemplateMap) \
V(TemplateObjectDescription) \
V(ThinString) \
V(TransitionArray) \

View File

@ -12,117 +12,41 @@
namespace v8 {
namespace internal {
bool TemplateObjectDescription::Equals(
TemplateObjectDescription const* that) const {
if (this->raw_strings()->length() == that->raw_strings()->length()) {
for (int i = this->raw_strings()->length(); --i >= 0;) {
if (this->raw_strings()->get(i) != that->raw_strings()->get(i)) {
return false;
}
}
return true;
}
return false;
}
// static
Handle<JSArray> TemplateObjectDescription::GetTemplateObject(
Handle<TemplateObjectDescription> description,
Handle<Context> native_context) {
DCHECK(native_context->IsNativeContext());
Isolate* const isolate = native_context->GetIsolate();
Handle<JSArray> TemplateObjectDescription::CreateTemplateObject(
Handle<TemplateObjectDescription> description) {
Isolate* const isolate = description->GetIsolate();
// Check if we already have a [[TemplateMap]] for the {native_context},
// and if not, just allocate one on the fly (which will be set below).
Handle<TemplateMap> template_map =
native_context->template_map()->IsUndefined(isolate)
? TemplateMap::New(isolate)
: handle(TemplateMap::cast(native_context->template_map()), isolate);
// Create the raw object from the {raw_strings}.
Handle<FixedArray> raw_strings(description->raw_strings(), isolate);
Handle<JSArray> raw_object = isolate->factory()->NewJSArrayWithElements(
raw_strings, PACKED_ELEMENTS, raw_strings->length(), TENURED);
// Check if we already have an appropriate entry.
Handle<JSArray> template_object;
if (!TemplateMap::Lookup(template_map, description)
.ToHandle(&template_object)) {
// Create the raw object from the {raw_strings}.
Handle<FixedArray> raw_strings(description->raw_strings(), isolate);
Handle<JSArray> raw_object = isolate->factory()->NewJSArrayWithElements(
raw_strings, PACKED_ELEMENTS, raw_strings->length(), TENURED);
// Create the template object from the {cooked_strings}.
Handle<FixedArray> cooked_strings(description->cooked_strings(), isolate);
Handle<JSArray> template_object = isolate->factory()->NewJSArrayWithElements(
cooked_strings, PACKED_ELEMENTS, cooked_strings->length(), TENURED);
// Create the template object from the {cooked_strings}.
Handle<FixedArray> cooked_strings(description->cooked_strings(), isolate);
template_object = isolate->factory()->NewJSArrayWithElements(
cooked_strings, PACKED_ELEMENTS, cooked_strings->length(), TENURED);
// Freeze the {raw_object}.
JSObject::SetIntegrityLevel(raw_object, FROZEN, kThrowOnError).ToChecked();
// Freeze the {raw_object}.
JSObject::SetIntegrityLevel(raw_object, FROZEN, kThrowOnError).ToChecked();
// Install a "raw" data property for {raw_object} on {template_object}.
PropertyDescriptor raw_desc;
raw_desc.set_value(raw_object);
raw_desc.set_configurable(false);
raw_desc.set_enumerable(false);
raw_desc.set_writable(false);
JSArray::DefineOwnProperty(isolate, template_object,
isolate->factory()->raw_string(), &raw_desc,
kThrowOnError)
.ToChecked();
// Install a "raw" data property for {raw_object} on {template_object}.
PropertyDescriptor raw_desc;
raw_desc.set_value(raw_object);
raw_desc.set_configurable(false);
raw_desc.set_enumerable(false);
raw_desc.set_writable(false);
JSArray::DefineOwnProperty(isolate, template_object,
isolate->factory()->raw_string(), &raw_desc,
kThrowOnError)
.ToChecked();
// Freeze the {template_object} as well.
JSObject::SetIntegrityLevel(template_object, FROZEN, kThrowOnError)
.ToChecked();
// Remember the {template_object} in the {template_map}.
template_map = TemplateMap::Add(template_map, description, template_object);
native_context->set_template_map(*template_map);
}
// Freeze the {template_object} as well.
JSObject::SetIntegrityLevel(template_object, FROZEN, kThrowOnError)
.ToChecked();
return template_object;
}
// static
bool TemplateMapShape::IsMatch(TemplateObjectDescription* key, Object* value) {
return key->Equals(TemplateObjectDescription::cast(value));
}
// static
uint32_t TemplateMapShape::Hash(Isolate* isolate,
TemplateObjectDescription* key) {
return key->hash();
}
// static
uint32_t TemplateMapShape::HashForObject(Isolate* isolate, Object* object) {
return Hash(isolate, TemplateObjectDescription::cast(object));
}
// static
Handle<TemplateMap> TemplateMap::New(Isolate* isolate) {
return HashTable::New(isolate, 0);
}
// static
MaybeHandle<JSArray> TemplateMap::Lookup(
Handle<TemplateMap> template_map, Handle<TemplateObjectDescription> key) {
int const entry = template_map->FindEntry(*key);
if (entry == kNotFound) return MaybeHandle<JSArray>();
int const index = EntryToIndex(entry);
return handle(JSArray::cast(template_map->get(index + 1)));
}
// static
Handle<TemplateMap> TemplateMap::Add(Handle<TemplateMap> template_map,
Handle<TemplateObjectDescription> key,
Handle<JSArray> value) {
DCHECK_EQ(kNotFound, template_map->FindEntry(*key));
template_map = EnsureCapacity(template_map, 1);
uint32_t const hash = ShapeT::Hash(key->GetIsolate(), *key);
int const entry = template_map->FindInsertionEntry(hash);
int const index = EntryToIndex(entry);
template_map->set(index + 0, *key);
template_map->set(index + 1, *value);
template_map->ElementAdded();
return template_map;
}
} // namespace internal
} // namespace v8

View File

@ -16,61 +16,25 @@ namespace internal {
// TemplateObjectDescription is a triple of hash, raw strings and cooked
// strings for tagged template literals. Used to communicate with the runtime
// for template object creation within the {Runtime_GetTemplateObject} method.
class TemplateObjectDescription final : public Tuple3 {
// for template object creation within the {Runtime_CreateTemplateObject}
// method.
class TemplateObjectDescription final : public Tuple2 {
public:
DECL_INT_ACCESSORS(hash)
DECL_ACCESSORS(raw_strings, FixedArray)
DECL_ACCESSORS(cooked_strings, FixedArray)
bool Equals(TemplateObjectDescription const* that) const;
static Handle<JSArray> GetTemplateObject(
Handle<TemplateObjectDescription> description,
Handle<Context> native_context);
static Handle<JSArray> CreateTemplateObject(
Handle<TemplateObjectDescription> description);
DECL_CAST(TemplateObjectDescription)
static constexpr int kHashOffset = kValue1Offset;
static constexpr int kRawStringsOffset = kValue2Offset;
static constexpr int kCookedStringsOffset = kValue3Offset;
static constexpr int kRawStringsOffset = kValue1Offset;
static constexpr int kCookedStringsOffset = kValue2Offset;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(TemplateObjectDescription);
};
class TemplateMapShape final : public BaseShape<TemplateObjectDescription*> {
public:
static bool IsMatch(TemplateObjectDescription* key, Object* value);
static uint32_t Hash(Isolate* isolate, TemplateObjectDescription* key);
static uint32_t HashForObject(Isolate* isolate, Object* object);
static constexpr int kPrefixSize = 0;
static constexpr int kEntrySize = 2;
};
class TemplateMap final : public HashTable<TemplateMap, TemplateMapShape> {
public:
static Handle<TemplateMap> New(Isolate* isolate);
// Tries to lookup the given {key} in the {template_map}. Returns the
// value if it's found, otherwise returns an empty MaybeHandle.
WARN_UNUSED_RESULT static MaybeHandle<JSArray> Lookup(
Handle<TemplateMap> template_map, Handle<TemplateObjectDescription> key);
// Adds the {key} / {value} pair to the {template_map} and returns the
// new TemplateMap (we might need to re-allocate). This assumes that
// there's no entry for {key} in the {template_map} already.
static Handle<TemplateMap> Add(Handle<TemplateMap> template_map,
Handle<TemplateObjectDescription> key,
Handle<JSArray> value);
DECL_CAST(TemplateMap)
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(TemplateMap);
};
} // namespace internal
} // namespace v8

View File

@ -3673,9 +3673,8 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
return expr;
} else {
// GetTemplateObject
const int32_t hash = ComputeTemplateLiteralHash(lit);
Expression* template_object =
factory()->NewGetTemplateObject(cooked_strings, raw_strings, hash, pos);
factory()->NewGetTemplateObject(cooked_strings, raw_strings, pos);
// Call TagFn
ZoneList<Expression*>* call_args =
@ -3688,51 +3687,6 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
namespace {
// http://burtleburtle.net/bob/hash/integer.html
uint32_t HalfAvalance(uint32_t a) {
a = (a + 0x479AB41D) + (a << 8);
a = (a ^ 0xE4AA10CE) ^ (a >> 5);
a = (a + 0x9942F0A6) - (a << 14);
a = (a ^ 0x5AEDD67D) ^ (a >> 3);
a = (a + 0x17BEA992) + (a << 7);
return a;
}
} // namespace
int32_t Parser::ComputeTemplateLiteralHash(const TemplateLiteral* lit) {
const ZoneList<const AstRawString*>* raw_strings = lit->raw();
int total = raw_strings->length();
DCHECK_GT(total, 0);
uint32_t running_hash = 0;
for (int index = 0; index < total; ++index) {
if (index) {
running_hash = StringHasher::ComputeRunningHashOneByte(
running_hash, "${}", 3);
}
const AstRawString* raw_string = raw_strings->at(index);
if (raw_string->is_one_byte()) {
const char* data = reinterpret_cast<const char*>(raw_string->raw_data());
running_hash = StringHasher::ComputeRunningHashOneByte(
running_hash, data, raw_string->length());
} else {
const uc16* data = reinterpret_cast<const uc16*>(raw_string->raw_data());
running_hash = StringHasher::ComputeRunningHash(running_hash, data,
raw_string->length());
}
}
// Pass {running_hash} throught a decent 'half avalance' hash function
// and take the most significant bits (in Smi range).
return static_cast<int32_t>(HalfAvalance(running_hash)) >>
(sizeof(int32_t) * CHAR_BIT - kSmiValueSize);
}
namespace {
bool OnlyLastArgIsSpread(ZoneList<Expression*>* args) {
for (int i = 0; i < args->length() - 1; i++) {
if (args->at(i)->IsSpread()) {

View File

@ -556,7 +556,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Expression* expression);
Expression* CloseTemplateLiteral(TemplateLiteralState* state, int start,
Expression* tag);
int32_t ComputeTemplateLiteralHash(const TemplateLiteral* lit);
ZoneList<Expression*>* PrepareSpreadArguments(ZoneList<Expression*>* list);
Expression* SpreadCall(Expression* function, ZoneList<Expression*>* args,

View File

@ -584,13 +584,12 @@ RUNTIME_FUNCTION(Runtime_CreateAsyncFromSyncIterator) {
Handle<JSReceiver>::cast(sync_iterator), next);
}
RUNTIME_FUNCTION(Runtime_GetTemplateObject) {
RUNTIME_FUNCTION(Runtime_CreateTemplateObject) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(TemplateObjectDescription, description, 0);
return *TemplateObjectDescription::GetTemplateObject(
description, isolate->native_context());
return *TemplateObjectDescription::CreateTemplateObject(description);
}
RUNTIME_FUNCTION(Runtime_ReportMessage) {

View File

@ -326,7 +326,7 @@ namespace internal {
F(Typeof, 1, 1) \
F(UnwindAndFindExceptionHandler, 0, 1) \
F(AllowDynamicFunction, 1, 1) \
F(GetTemplateObject, 1, 1) \
F(CreateTemplateObject, 1, 1) \
F(ReportMessage, 1, 1)
#define FOR_EACH_INTRINSIC_LITERALS(F) \

View File

@ -342,27 +342,30 @@ var obj = {
var a = 1;
var b = 2;
tag`head${a}tail`;
tag`head${b}tail`;
// Call-sites are cached by ParseNode. Same tag call in a loop
// means same template object
for (var i = 0; i < 2; ++i) {
tag`head${i == 0 ? a : b}tail`;
}
assertEquals(2, callSites.length);
assertSame(callSites[0], callSites[1]);
eval("tag`head${a}tail`");
assertEquals(3, callSites.length);
assertSame(callSites[1], callSites[2]);
eval("tag`head${b}tail`");
// Tag calls within eval() never have the same ParseNode as the same tag
// call from a different eval() invocation.
for (var i = 0; i < 2; ++i) {
eval("tag`head${i == 0 ? a : b}tail`");
}
assertEquals(4, callSites.length);
assertSame(callSites[2], callSites[3]);
assertTrue(callSites[1] !== callSites[2]);
assertTrue(callSites[2] !== callSites[3]);
(new Function("tag", "a", "b", "return tag`head${a}tail`;"))(tag, 1, 2);
assertEquals(5, callSites.length);
assertSame(callSites[3], callSites[4]);
assertTrue(callSites[3] !== callSites[4]);
(new Function("tag", "a", "b", "return tag`head${b}tail`;"))(tag, 1, 2);
assertEquals(6, callSites.length);
assertSame(callSites[4], callSites[5]);
assertTrue(callSites[4] !== callSites[5]);
callSites = [];
@ -374,17 +377,19 @@ var obj = {
callSites = [];
eval("tag`\\\r\n\\\n\\\r`");
eval("tag`\\\r\n\\\n\\\r`");
for (var i = 0; i < 2; ++i) {
eval("tag`\\\r\n\\\n\\\r`");
}
assertEquals(2, callSites.length);
assertSame(callSites[0], callSites[1]);
assertTrue(callSites[0] !== callSites[1]);
assertEquals("", callSites[0][0]);
assertEquals("\\\n\\\n\\\n", callSites[0].raw[0]);
callSites = [];
tag`\uc548\ub155`;
tag`\uc548\ub155`;
for (var i = 0; i < 2; ++i) {
tag`\uc548\ub155`;
}
assertEquals(2, callSites.length);
assertSame(callSites[0], callSites[1]);
assertEquals("안녕", callSites[0][0]);

View File

@ -292,6 +292,14 @@
'language/statements/try/tco-finally': [SKIP],
'language/statements/while/tco-body': [SKIP],
# https://github.com/tc39/test262/pull/972
'language/expressions/tagged-template/cache-identical-source-new-function': [FAIL],
'language/expressions/tagged-template/cache-identical-source': [FAIL],
'language/expressions/tagged-template/cache-differing-expressions-eval': [FAIL],
'language/expressions/tagged-template/cache-identical-source-eval': [FAIL],
'language/expressions/tagged-template/cache-differing-expressions-new-function': [FAIL],
'language/expressions/tagged-template/cache-differing-expressions': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=5064
'language/expressions/arrow-function/dflt-params-duplicates': [FAIL],
'language/expressions/async-arrow-function/dflt-params-duplicates': [FAIL],

View File

@ -176,7 +176,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
builder.CreateObjectLiteral(0, 0, 0, reg);
// Emit tagged template operations.
builder.GetTemplateObject(0);
builder.GetTemplateObject(0, 0);
// Call operations.
builder.CallAnyReceiver(reg, reg_list, 1)