Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index 286fbe7c97da52bdfd5ff7d32fafc8f6d5d81e9f..0881cddd6fa00d84779ccd5039e5f7b5b5eaa890 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -16,6 +16,7 @@ |
#include "vm/flags.h" |
#include "vm/growable_array.h" |
#include "vm/handles.h" |
+#include "vm/hash_table.h" |
#include "vm/heap.h" |
#include "vm/isolate.h" |
#include "vm/longjump.h" |
@@ -11885,6 +11886,65 @@ bool Parser::IsInstantiatorRequired() const { |
} |
+class ConstMapKeyEqualsTraits { |
+ public: |
+ static bool IsMatch(const Object& a, const Object& b) { |
+ return String::Cast(a).Equals(String::Cast(b)); |
+ } |
+ static bool IsMatch(const char* key, const Object& b) { |
+ return String::Cast(b).Equals(key); |
+ } |
+ static uword Hash(const Object& obj) { |
+ return String::Cast(obj).Hash(); |
+ } |
+ static uword Hash(const char* key) { |
+ return String::Hash(key, strlen(key)); |
+ } |
+}; |
+typedef UnorderedHashMap<ConstMapKeyEqualsTraits> ConstantsMap; |
+ |
+ |
+void Parser::CacheConstantValue(intptr_t token_pos, const Instance& value) { |
+ String& key = String::Handle(Z, script_.url()); |
+ String& suffix = |
+ String::Handle(Z, String::NewFormatted("_%" Pd "", token_pos)); |
+ key = Symbols::FromConcat(key, suffix); |
+ if (isolate()->object_store()->compile_time_constants() == Array::null()) { |
+ const intptr_t kInitialConstMapSize = 16; |
+ isolate()->object_store()->set_compile_time_constants( |
+ Array::Handle(Z, HashTables::New<ConstantsMap>(kInitialConstMapSize))); |
+ } |
+ ConstantsMap constants(isolate()->object_store()->compile_time_constants()); |
+ constants.UpdateOrInsert(key, value); |
+ if (FLAG_compiler_stats) { |
+ isolate_->compiler_stats()->num_cached_consts = constants.NumOccupied(); |
+ } |
+ isolate()->object_store()->set_compile_time_constants(constants.Release()); |
+} |
+ |
+ |
+bool Parser::GetCachedConstant(intptr_t token_pos, Instance* value) { |
+ if (isolate()->object_store()->compile_time_constants() == Array::null()) { |
+ return false; |
+ } |
+ // We don't want to allocate anything in the heap here since this code |
+ // is called from the optimizing compiler in the background thread. Allocate |
+ // the key value in the zone instead. |
+ const char* key = Z->PrintToString("%s_%" Pd "", |
+ String::Handle(Z, script_.url()).ToCString(), |
+ token_pos); |
+ ConstantsMap constants(isolate()->object_store()->compile_time_constants()); |
+ bool is_present = false; |
+ *value ^= constants.GetOrNull<const char *>(key, &is_present); |
+ ASSERT(constants.Release().raw() == |
+ isolate()->object_store()->compile_time_constants()); |
+ if (FLAG_compiler_stats && is_present) { |
+ isolate_->compiler_stats()->num_const_cache_hits++; |
+ } |
+ return is_present; |
+} |
+ |
+ |
RawInstance* Parser::TryCanonicalize(const Instance& instance, |
intptr_t token_pos) { |
if (instance.IsNull()) { |
@@ -12507,6 +12567,15 @@ AstNode* Parser::ParseListLiteral(intptr_t type_pos, |
ASSERT(type_pos >= 0); |
ASSERT(CurrentToken() == Token::kLBRACK || CurrentToken() == Token::kINDEX); |
const intptr_t literal_pos = TokenPos(); |
+ |
+ if (is_const) { |
+ Instance& existing_const = Instance::ZoneHandle(Z); |
+ if (GetCachedConstant(literal_pos, &existing_const)) { |
+ SkipListLiteral(); |
+ return new(Z) LiteralNode(literal_pos, existing_const); |
+ } |
+ } |
+ |
bool is_empty_literal = CurrentToken() == Token::kINDEX; |
ConsumeToken(); |
@@ -12574,8 +12643,8 @@ AstNode* Parser::ParseListLiteral(intptr_t type_pos, |
if (is_const) { |
// Allocate and initialize the const list at compile time. |
- Array& const_list = |
- Array::ZoneHandle(Z, Array::New(element_list.length(), Heap::kOld)); |
+ Array& const_list = Array::ZoneHandle(Z, |
+ Array::New(element_list.length(), Heap::kOld)); |
const_list.SetTypeArguments( |
TypeArguments::Handle(Z, list_type_arguments.Canonicalize())); |
Error& malformed_error = Error::Handle(Z); |
@@ -12607,6 +12676,7 @@ AstNode* Parser::ParseListLiteral(intptr_t type_pos, |
} |
const_list.MakeImmutable(); |
const_list ^= TryCanonicalize(const_list, literal_pos); |
+ CacheConstantValue(literal_pos, const_list); |
return new(Z) LiteralNode(literal_pos, const_list); |
} else { |
// Factory call at runtime. |
@@ -12700,8 +12770,16 @@ AstNode* Parser::ParseMapLiteral(intptr_t type_pos, |
ASSERT(type_pos >= 0); |
ASSERT(CurrentToken() == Token::kLBRACE); |
const intptr_t literal_pos = TokenPos(); |
- ConsumeToken(); |
+ if (is_const) { |
+ Instance& existing_const = Instance::ZoneHandle(Z); |
+ if (GetCachedConstant(literal_pos, &existing_const)) { |
+ SkipMapLiteral(); |
+ return new(Z) LiteralNode(literal_pos, existing_const); |
+ } |
+ } |
+ |
+ ConsumeToken(); // Opening brace. |
AbstractType& key_type = Type::ZoneHandle(Z, Type::DynamicType()); |
AbstractType& value_type = Type::ZoneHandle(Z, Type::DynamicType()); |
TypeArguments& map_type_arguments = |
@@ -12858,6 +12936,7 @@ AstNode* Parser::ParseMapLiteral(intptr_t type_pos, |
"error executing const Map constructor"); |
} else { |
const Instance& const_instance = Instance::Cast(constructor_result); |
+ CacheConstantValue(literal_pos, const_instance); |
return new(Z) LiteralNode( |
literal_pos, Instance::ZoneHandle(Z, const_instance.raw())); |
} |
@@ -13401,41 +13480,43 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { |
"const object creation", |
external_constructor_name.ToCString()); |
} |
- const Object& constructor_result = Object::Handle(Z, |
- EvaluateConstConstructorCall(type_class, |
- type_arguments, |
- constructor, |
- arguments)); |
- if (constructor_result.IsUnhandledException()) { |
- // It's a compile-time error if invocation of a const constructor |
- // call fails. |
- ReportErrors(Error::Cast(constructor_result), |
- script_, new_pos, |
- "error while evaluating const constructor"); |
+ |
+ Instance& const_instance = Instance::ZoneHandle(Z); |
+ if (GetCachedConstant(new_pos, &const_instance)) { |
+ // Cache hit, nothing else to do. |
} else { |
- // Const constructors can return null in the case where a const native |
- // factory returns a null value. Thus we cannot use a Instance::Cast here. |
- Instance& const_instance = Instance::Handle(Z); |
+ Object& constructor_result = Object::Handle(Z, |
+ EvaluateConstConstructorCall(type_class, |
+ type_arguments, |
+ constructor, |
+ arguments)); |
+ if (constructor_result.IsUnhandledException()) { |
+ // It's a compile-time error if invocation of a const constructor |
+ // call fails. |
+ ReportErrors(Error::Cast(constructor_result), |
+ script_, new_pos, |
+ "error while evaluating const constructor"); |
+ } |
const_instance ^= constructor_result.raw(); |
- new_object = new(Z) LiteralNode( |
- new_pos, Instance::ZoneHandle(Z, const_instance.raw())); |
- if (!type_bound.IsNull()) { |
- ASSERT(!type_bound.IsMalformed()); |
- Error& malformed_error = Error::Handle(Z); |
- ASSERT(!is_top_level_); // We cannot check unresolved types. |
- if (!const_instance.IsInstanceOf(type_bound, |
- TypeArguments::Handle(Z), |
- &malformed_error)) { |
- type_bound = ClassFinalizer::NewFinalizedMalformedType( |
- malformed_error, |
- script_, |
- new_pos, |
- "const factory result is not an instance of '%s'", |
- String::Handle(Z, type_bound.UserVisibleName()).ToCString()); |
- new_object = ThrowTypeError(new_pos, type_bound); |
- } |
- type_bound = AbstractType::null(); |
+ CacheConstantValue(new_pos, const_instance); |
+ } |
+ new_object = new(Z) LiteralNode(new_pos, const_instance); |
+ if (!type_bound.IsNull()) { |
+ ASSERT(!type_bound.IsMalformed()); |
+ Error& malformed_error = Error::Handle(Z); |
+ ASSERT(!is_top_level_); // We cannot check unresolved types. |
+ if (!const_instance.IsInstanceOf(type_bound, |
+ TypeArguments::Handle(Z), |
+ &malformed_error)) { |
+ type_bound = ClassFinalizer::NewFinalizedMalformedType( |
+ malformed_error, |
+ script_, |
+ new_pos, |
+ "const factory result is not an instance of '%s'", |
+ String::Handle(Z, type_bound.UserVisibleName()).ToCString()); |
+ new_object = ThrowTypeError(new_pos, type_bound); |
} |
+ type_bound = AbstractType::null(); |
} |
} else { |
CheckConstructorCallTypeArguments(new_pos, constructor, type_arguments); |