Index: runtime/vm/code_generator.cc |
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc |
index de63989ec9f953e6121c4bfaf8ca5e0a55a69f02..4e6a05ca231d28c7c5e9bd6c057c4069415130f3 100644 |
--- a/runtime/vm/code_generator.cc |
+++ b/runtime/vm/code_generator.cc |
@@ -234,16 +234,23 @@ DEFINE_RUNTIME_ENTRY(AllocateObject, 2) { |
// Instantiate type. |
// Arg0: uninstantiated type. |
// Arg1: instantiator type arguments. |
+// Arg2: function type arguments. |
// Return value: instantiated type. |
-DEFINE_RUNTIME_ENTRY(InstantiateType, 2) { |
+DEFINE_RUNTIME_ENTRY(InstantiateType, 3) { |
AbstractType& type = AbstractType::CheckedHandle(zone, arguments.ArgAt(0)); |
- const TypeArguments& instantiator = |
+ const TypeArguments& instantiator_type_arguments = |
TypeArguments::CheckedHandle(zone, arguments.ArgAt(1)); |
+ const TypeArguments& function_type_arguments = |
+ TypeArguments::CheckedHandle(zone, arguments.ArgAt(2)); |
ASSERT(!type.IsNull() && !type.IsInstantiated()); |
- ASSERT(instantiator.IsNull() || instantiator.IsInstantiated()); |
+ ASSERT(instantiator_type_arguments.IsNull() || |
+ instantiator_type_arguments.IsInstantiated()); |
+ ASSERT(function_type_arguments.IsNull() || |
+ function_type_arguments.IsInstantiated()); |
Error& bound_error = Error::Handle(zone); |
type = |
- type.InstantiateFrom(instantiator, &bound_error, NULL, NULL, Heap::kOld); |
+ type.InstantiateFrom(instantiator_type_arguments, function_type_arguments, |
+ &bound_error, NULL, NULL, Heap::kOld); |
if (!bound_error.IsNull()) { |
// Throw a dynamic type error. |
const TokenPosition location = GetCallerLocation(); |
@@ -267,21 +274,27 @@ DEFINE_RUNTIME_ENTRY(InstantiateType, 2) { |
// Instantiate type arguments. |
// Arg0: uninstantiated type arguments. |
// Arg1: instantiator type arguments. |
+// Arg2: function type arguments. |
// Return value: instantiated type arguments. |
-DEFINE_RUNTIME_ENTRY(InstantiateTypeArguments, 2) { |
+DEFINE_RUNTIME_ENTRY(InstantiateTypeArguments, 3) { |
TypeArguments& type_arguments = |
TypeArguments::CheckedHandle(zone, arguments.ArgAt(0)); |
- const TypeArguments& instantiator = |
+ const TypeArguments& instantiator_type_arguments = |
TypeArguments::CheckedHandle(zone, arguments.ArgAt(1)); |
+ const TypeArguments& function_type_arguments = |
+ TypeArguments::CheckedHandle(zone, arguments.ArgAt(2)); |
ASSERT(!type_arguments.IsNull() && !type_arguments.IsInstantiated()); |
- ASSERT(instantiator.IsNull() || instantiator.IsInstantiated()); |
+ ASSERT(instantiator_type_arguments.IsNull() || |
+ instantiator_type_arguments.IsInstantiated()); |
+ ASSERT(function_type_arguments.IsNull() || |
+ function_type_arguments.IsInstantiated()); |
// Code inlined in the caller should have optimized the case where the |
// instantiator can be reused as type argument vector. |
ASSERT(!type_arguments.IsUninstantiatedIdentity()); |
if (isolate->type_checks()) { |
Error& bound_error = Error::Handle(zone); |
type_arguments = type_arguments.InstantiateAndCanonicalizeFrom( |
- instantiator, &bound_error); |
+ instantiator_type_arguments, function_type_arguments, &bound_error); |
if (!bound_error.IsNull()) { |
// Throw a dynamic type error. |
const TokenPosition location = GetCallerLocation(); |
@@ -293,8 +306,8 @@ DEFINE_RUNTIME_ENTRY(InstantiateTypeArguments, 2) { |
UNREACHABLE(); |
} |
} else { |
- type_arguments = |
- type_arguments.InstantiateAndCanonicalizeFrom(instantiator, NULL); |
+ type_arguments = type_arguments.InstantiateAndCanonicalizeFrom( |
+ instantiator_type_arguments, function_type_arguments, NULL); |
} |
ASSERT(type_arguments.IsNull() || type_arguments.IsInstantiated()); |
arguments.SetReturn(type_arguments); |
@@ -333,6 +346,7 @@ static void PrintTypeCheck(const char* message, |
const Instance& instance, |
const AbstractType& type, |
const TypeArguments& instantiator_type_arguments, |
+ const TypeArguments& function_type_arguments, |
const Bool& result) { |
DartFrameIterator iterator; |
StackFrame* caller_frame = iterator.NextFrame(); |
@@ -353,7 +367,8 @@ static void PrintTypeCheck(const char* message, |
Error& bound_error = Error::Handle(); |
const AbstractType& instantiated_type = |
AbstractType::Handle(type.InstantiateFrom( |
- instantiator_type_arguments, &bound_error, NULL, NULL, Heap::kOld)); |
+ instantiator_type_arguments, function_type_arguments, &bound_error, |
+ NULL, NULL, Heap::kOld)); |
OS::PrintErr("%s: '%s' %s '%s' instantiated from '%s' (pc: %#" Px ").\n", |
message, String::Handle(instance_type.Name()).ToCString(), |
(result.raw() == Bool::True().raw()) ? "is" : "is !", |
@@ -369,16 +384,18 @@ static void PrintTypeCheck(const char* message, |
} |
-// This updates the type test cache, an array containing 4-value elements |
+// This updates the type test cache, an array containing 5-value elements |
// (instance class (or function if the instance is a closure), instance type |
-// arguments, instantiator type arguments and test_result). It can be applied to |
-// classes with type arguments in which case it contains just the result of the |
-// class subtype test, not including the evaluation of type arguments. |
+// arguments, instantiator type arguments, function type arguments, |
+// and test_result). It can be applied to classes with type arguments in which |
+// case it contains just the result of the class subtype test, not including the |
+// evaluation of type arguments. |
// This operation is currently very slow (lookup of code is not efficient yet). |
static void UpdateTypeTestCache( |
const Instance& instance, |
const AbstractType& type, |
const TypeArguments& instantiator_type_arguments, |
+ const TypeArguments& function_type_arguments, |
const Bool& result, |
const SubtypeTestCache& new_cache) { |
// Since the test is expensive, don't do it unless necessary. |
@@ -396,6 +413,17 @@ static void UpdateTypeTestCache( |
} |
return; |
} |
+ // If the type is uninstantiated and refers to parent function type |
+ // parameters, the context is required in the type test and the cache |
+ // therefore cannot be used. |
+ if (!type.IsInstantiated(kParentFunctions)) { |
+ if (FLAG_trace_type_checks) { |
+ OS::Print( |
+ "UpdateTypeTestCache: type refers to parent function's type " |
+ "parameters\n"); |
+ } |
+ return; |
+ } |
const Class& instance_class = Class::Handle(instance.clazz()); |
Object& instance_class_id_or_function = Object::Handle(); |
TypeArguments& instance_type_arguments = TypeArguments::Handle(); |
@@ -417,48 +445,61 @@ static void UpdateTypeTestCache( |
instance_type_arguments.IsCanonical()); |
ASSERT(instantiator_type_arguments.IsNull() || |
instantiator_type_arguments.IsCanonical()); |
+ ASSERT(function_type_arguments.IsNull() || |
+ function_type_arguments.IsCanonical()); |
Object& last_instance_class_id_or_function = Object::Handle(); |
TypeArguments& last_instance_type_arguments = TypeArguments::Handle(); |
TypeArguments& last_instantiator_type_arguments = TypeArguments::Handle(); |
+ TypeArguments& last_function_type_arguments = TypeArguments::Handle(); |
Bool& last_result = Bool::Handle(); |
for (intptr_t i = 0; i < len; ++i) { |
new_cache.GetCheck(i, &last_instance_class_id_or_function, |
&last_instance_type_arguments, |
- &last_instantiator_type_arguments, &last_result); |
+ &last_instantiator_type_arguments, |
+ &last_function_type_arguments, &last_result); |
if ((last_instance_class_id_or_function.raw() == |
instance_class_id_or_function.raw()) && |
(last_instance_type_arguments.raw() == instance_type_arguments.raw()) && |
(last_instantiator_type_arguments.raw() == |
- instantiator_type_arguments.raw())) { |
+ instantiator_type_arguments.raw()) && |
+ (last_function_type_arguments.raw() == |
+ last_function_type_arguments.raw())) { |
OS::PrintErr(" Error in test cache %p ix: %" Pd ",", new_cache.raw(), i); |
PrintTypeCheck(" duplicate cache entry", instance, type, |
- instantiator_type_arguments, result); |
+ instantiator_type_arguments, function_type_arguments, |
+ result); |
UNREACHABLE(); |
return; |
} |
} |
#endif |
new_cache.AddCheck(instance_class_id_or_function, instance_type_arguments, |
- instantiator_type_arguments, result); |
+ instantiator_type_arguments, function_type_arguments, |
+ result); |
if (FLAG_trace_type_checks) { |
AbstractType& test_type = AbstractType::Handle(type.raw()); |
if (!test_type.IsInstantiated()) { |
Error& bound_error = Error::Handle(); |
test_type = type.InstantiateFrom(instantiator_type_arguments, |
- &bound_error, NULL, NULL, Heap::kNew); |
+ function_type_arguments, &bound_error, |
+ NULL, NULL, Heap::kNew); |
ASSERT(bound_error.IsNull()); // Malbounded types are not optimized. |
} |
OS::PrintErr( |
" Updated test cache %p ix: %" Pd |
" with " |
- "(cid-or-fun: %p, type-args: %p, instantiator: %p, result: %s)\n" |
+ "(cid-or-fun: %p, type-args: %p, i-type-args: %p, f-type-args: %p, " |
+ "result: %s)\n" |
" instance [class: (%p '%s' cid: %" Pd |
"), type-args: %p %s]\n" |
- " test-type [class: (%p '%s' cid: %" Pd "), in-type-args: %p %s]\n", |
+ " test-type [class: (%p '%s' cid: %" Pd |
+ "), i-type-args: %p %s, " |
+ ", f-type-args: %p %s]\n", |
new_cache.raw(), len, |
instance_class_id_or_function.raw(), instance_type_arguments.raw(), |
- instantiator_type_arguments.raw(), result.ToCString(), |
+ instantiator_type_arguments.raw(), instantiator_type_arguments.raw(), |
+ result.ToCString(), |
instance_class.raw(), String::Handle(instance_class.Name()).ToCString(), |
instance_class.id(), instance_type_arguments.raw(), |
@@ -469,6 +510,8 @@ static void UpdateTypeTestCache( |
.ToCString(), |
Class::Handle(test_type.type_class()).id(), |
instantiator_type_arguments.raw(), |
+ instantiator_type_arguments.ToCString(), |
+ instantiator_type_arguments.raw(), |
instantiator_type_arguments.ToCString()); |
} |
} |
@@ -479,26 +522,30 @@ static void UpdateTypeTestCache( |
// Arg0: instance being checked. |
// Arg1: type. |
// Arg2: type arguments of the instantiator of the type. |
-// Arg3: SubtypeTestCache. |
+// Arg3: type arguments of the function of the type. |
+// Arg4: SubtypeTestCache. |
// Return value: true or false, or may throw a type error in checked mode. |
-DEFINE_RUNTIME_ENTRY(Instanceof, 4) { |
+DEFINE_RUNTIME_ENTRY(Instanceof, 5) { |
const Instance& instance = Instance::CheckedHandle(zone, arguments.ArgAt(0)); |
const AbstractType& type = |
AbstractType::CheckedHandle(zone, arguments.ArgAt(1)); |
const TypeArguments& instantiator_type_arguments = |
TypeArguments::CheckedHandle(zone, arguments.ArgAt(2)); |
+ const TypeArguments& function_type_arguments = |
+ TypeArguments::CheckedHandle(zone, arguments.ArgAt(3)); |
const SubtypeTestCache& cache = |
- SubtypeTestCache::CheckedHandle(zone, arguments.ArgAt(3)); |
+ SubtypeTestCache::CheckedHandle(zone, arguments.ArgAt(4)); |
ASSERT(type.IsFinalized()); |
ASSERT(!type.IsMalformed()); // Already checked in code generator. |
ASSERT(!type.IsMalbounded()); // Already checked in code generator. |
ASSERT(!type.IsDynamicType()); // No need to check assignment. |
Error& bound_error = Error::Handle(zone); |
- const Bool& result = Bool::Get( |
- instance.IsInstanceOf(type, instantiator_type_arguments, &bound_error)); |
+ const Bool& result = |
+ Bool::Get(instance.IsInstanceOf(type, instantiator_type_arguments, |
+ function_type_arguments, &bound_error)); |
if (FLAG_trace_type_checks) { |
PrintTypeCheck("InstanceOf", instance, type, instantiator_type_arguments, |
- result); |
+ function_type_arguments, result); |
} |
if (!result.value() && !bound_error.IsNull()) { |
// Throw a dynamic type error only if the instanceof test fails. |
@@ -510,8 +557,8 @@ DEFINE_RUNTIME_ENTRY(Instanceof, 4) { |
Symbols::Empty(), bound_error_message); |
UNREACHABLE(); |
} |
- UpdateTypeTestCache(instance, type, instantiator_type_arguments, result, |
- cache); |
+ UpdateTypeTestCache(instance, type, instantiator_type_arguments, |
+ function_type_arguments, result, cache); |
arguments.SetReturn(result); |
} |
@@ -521,31 +568,36 @@ DEFINE_RUNTIME_ENTRY(Instanceof, 4) { |
// Arg0: instance being assigned. |
// Arg1: type being assigned to. |
// Arg2: type arguments of the instantiator of the type being assigned to. |
-// Arg3: name of variable being assigned to. |
-// Arg4: SubtypeTestCache. |
+// Arg3: type arguments of the function of the type being assigned to. |
+// Arg4: name of variable being assigned to. |
+// Arg5: SubtypeTestCache. |
// Return value: instance if a subtype, otherwise throw a TypeError. |
-DEFINE_RUNTIME_ENTRY(TypeCheck, 5) { |
+DEFINE_RUNTIME_ENTRY(TypeCheck, 6) { |
const Instance& src_instance = |
Instance::CheckedHandle(zone, arguments.ArgAt(0)); |
AbstractType& dst_type = |
AbstractType::CheckedHandle(zone, arguments.ArgAt(1)); |
const TypeArguments& instantiator_type_arguments = |
TypeArguments::CheckedHandle(zone, arguments.ArgAt(2)); |
- const String& dst_name = String::CheckedHandle(zone, arguments.ArgAt(3)); |
+ const TypeArguments& function_type_arguments = |
+ TypeArguments::CheckedHandle(zone, arguments.ArgAt(3)); |
+ const String& dst_name = String::CheckedHandle(zone, arguments.ArgAt(4)); |
const SubtypeTestCache& cache = |
- SubtypeTestCache::CheckedHandle(zone, arguments.ArgAt(4)); |
+ SubtypeTestCache::CheckedHandle(zone, arguments.ArgAt(5)); |
ASSERT(!dst_type.IsMalformed()); // Already checked in code generator. |
ASSERT(!dst_type.IsMalbounded()); // Already checked in code generator. |
ASSERT(!dst_type.IsDynamicType()); // No need to check assignment. |
ASSERT(!src_instance.IsNull()); // Already checked in inlined code. |
Error& bound_error = Error::Handle(zone); |
- const bool is_instance_of = src_instance.IsInstanceOf( |
- dst_type, instantiator_type_arguments, &bound_error); |
+ const bool is_instance_of = |
+ src_instance.IsInstanceOf(dst_type, instantiator_type_arguments, |
+ function_type_arguments, &bound_error); |
if (FLAG_trace_type_checks) { |
PrintTypeCheck("TypeCheck", src_instance, dst_type, |
- instantiator_type_arguments, Bool::Get(is_instance_of)); |
+ instantiator_type_arguments, function_type_arguments, |
+ Bool::Get(is_instance_of)); |
} |
if (!is_instance_of) { |
// Throw a dynamic type error. |
@@ -554,8 +606,9 @@ DEFINE_RUNTIME_ENTRY(TypeCheck, 5) { |
AbstractType::Handle(zone, src_instance.GetType(Heap::kNew)); |
if (!dst_type.IsInstantiated()) { |
// Instantiate dst_type before reporting the error. |
- dst_type = dst_type.InstantiateFrom(instantiator_type_arguments, NULL, |
- NULL, NULL, Heap::kNew); |
+ dst_type = dst_type.InstantiateFrom(instantiator_type_arguments, |
+ function_type_arguments, NULL, NULL, |
+ NULL, Heap::kNew); |
// Note that instantiated dst_type may be malbounded. |
} |
String& bound_error_message = String::Handle(zone); |
@@ -568,7 +621,7 @@ DEFINE_RUNTIME_ENTRY(TypeCheck, 5) { |
UNREACHABLE(); |
} |
UpdateTypeTestCache(src_instance, dst_type, instantiator_type_arguments, |
- Bool::True(), cache); |
+ function_type_arguments, Bool::True(), cache); |
arguments.SetReturn(src_instance); |
} |
@@ -806,8 +859,8 @@ static RawFunction* ComputeTypeCheckTarget(const Instance& receiver, |
const AbstractType& type, |
const ArgumentsDescriptor& desc) { |
Error& error = Error::Handle(); |
- bool result = |
- receiver.IsInstanceOf(type, Object::null_type_arguments(), &error); |
+ bool result = receiver.IsInstanceOf(type, Object::null_type_arguments(), |
+ Object::null_type_arguments(), &error); |
ASSERT(error.IsNull()); |
ObjectStore* store = Isolate::Current()->object_store(); |
const Function& target = |