[vm,dyn_modules] Support error cases during hot reload when rebinding direct calls in bytecode
TEST=vm/cc/IsolateReload Change-Id: Ib989d2d951a342e4725b99b1651e8ea298ba2f64 Cq-Include-Trybots: luci.dart.try:vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/441826 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
21ce423b26
commit
6082256159
File diff suppressed because it is too large
Load Diff
@@ -2030,7 +2030,15 @@ SwitchDispatch:
|
||||
const uint32_t kidx = rD;
|
||||
|
||||
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
|
||||
*++SP = LOAD_CONSTANT(kidx);
|
||||
ObjectPtr target = LOAD_CONSTANT(kidx);
|
||||
*++SP = target;
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
|
||||
if (target->IsArray()) {
|
||||
// Hot reload failed to find a suitable target for this call.
|
||||
goto ThrowNoSuchMethodError;
|
||||
}
|
||||
#endif
|
||||
ASSERT(target->IsFunction());
|
||||
ObjectPtr* call_base = SP - argc;
|
||||
ObjectPtr* call_top = SP;
|
||||
argdesc_ = static_cast<ArrayPtr>(LOAD_CONSTANT(kidx + 1));
|
||||
@@ -2051,7 +2059,15 @@ SwitchDispatch:
|
||||
const uint32_t kidx = rD;
|
||||
|
||||
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
|
||||
*++SP = LOAD_CONSTANT(kidx);
|
||||
ObjectPtr target = LOAD_CONSTANT(kidx);
|
||||
*++SP = target;
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
|
||||
if (target->IsArray()) {
|
||||
// Hot reload failed to find a suitable target for this call.
|
||||
goto ThrowNoSuchMethodError;
|
||||
}
|
||||
#endif
|
||||
ASSERT(target->IsFunction());
|
||||
ObjectPtr* call_base = SP - argc;
|
||||
ObjectPtr* call_top = SP;
|
||||
argdesc_ = static_cast<ArrayPtr>(LOAD_CONSTANT(kidx + 1));
|
||||
@@ -4015,6 +4031,18 @@ SwitchDispatch:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
|
||||
{
|
||||
ThrowNoSuchMethodError:
|
||||
// SP[0] contains arguments.
|
||||
SP[1] = 0; // Unused space for result.
|
||||
Exit(thread, FP, SP + 2, pc);
|
||||
INVOKE_RUNTIME(DRT_NoSuchMethodError,
|
||||
NativeArguments(thread, 1, SP, SP + 1));
|
||||
UNREACHABLE();
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
|
||||
|
||||
// Exception handling helper. Gets handler FP and PC from the Interpreter
|
||||
// where they were stored by Interpreter::Longjmp and proceeds to execute the
|
||||
// handler. Corner case: handler PC can be a fake marker that marks entry
|
||||
|
||||
@@ -420,12 +420,15 @@ class CallSiteResetter : public ValueObject {
|
||||
void RebindBytecode(const Bytecode& bytecode);
|
||||
|
||||
private:
|
||||
Thread* thread_;
|
||||
Zone* zone_;
|
||||
Instructions& instrs_;
|
||||
ObjectPool& pool_;
|
||||
Object& object_;
|
||||
String& name_;
|
||||
Class& old_cls_;
|
||||
Class& new_cls_;
|
||||
Library& old_lib_;
|
||||
Library& new_lib_;
|
||||
Function& new_function_;
|
||||
Field& new_field_;
|
||||
|
||||
+100
-11
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "vm/object.h"
|
||||
|
||||
#include "lib/invocation_mirror.h"
|
||||
#include "platform/unaligned.h"
|
||||
#include "vm/code_patcher.h"
|
||||
#include "vm/dart_entry.h"
|
||||
@@ -41,12 +42,15 @@ void CallSiteResetter::ZeroEdgeCounters(const Function& function) {
|
||||
}
|
||||
|
||||
CallSiteResetter::CallSiteResetter(Zone* zone)
|
||||
: zone_(zone),
|
||||
: thread_(Thread::Current()),
|
||||
zone_(zone),
|
||||
instrs_(Instructions::Handle(zone)),
|
||||
pool_(ObjectPool::Handle(zone)),
|
||||
object_(Object::Handle(zone)),
|
||||
name_(String::Handle(zone)),
|
||||
old_cls_(Class::Handle(zone)),
|
||||
new_cls_(Class::Handle(zone)),
|
||||
old_lib_(Library::Handle(zone)),
|
||||
new_lib_(Library::Handle(zone)),
|
||||
new_function_(Function::Handle(zone)),
|
||||
new_field_(Field::Handle(zone)),
|
||||
@@ -840,6 +844,55 @@ void CallSiteResetter::Reset(const ICData& ic) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
static ArrayPtr PrepareNoSuchMethodErrorArguments(const Function& target,
|
||||
bool incompatible_arguments) {
|
||||
InvocationMirror::Kind kind = InvocationMirror::Kind::kMethod;
|
||||
if (target.IsImplicitGetterFunction() || target.IsGetterFunction()) {
|
||||
kind = InvocationMirror::kGetter;
|
||||
} else if (target.IsImplicitSetterFunction() || target.IsSetterFunction()) {
|
||||
kind = InvocationMirror::kSetter;
|
||||
}
|
||||
const Class& owner = Class::Handle(target.Owner());
|
||||
auto& receiver = Instance::Handle();
|
||||
InvocationMirror::Level level;
|
||||
if (owner.IsTopLevel()) {
|
||||
if (incompatible_arguments) {
|
||||
receiver = target.UserVisibleSignature();
|
||||
}
|
||||
level = InvocationMirror::Level::kTopLevel;
|
||||
} else {
|
||||
receiver = owner.RareType();
|
||||
if (target.IsConstructor()) {
|
||||
level = InvocationMirror::Level::kConstructor;
|
||||
} else {
|
||||
level = InvocationMirror::Level::kStatic;
|
||||
}
|
||||
}
|
||||
const auto& member_name = String::Handle(target.name());
|
||||
const auto& invocation_type =
|
||||
Smi::Handle(Smi::New(InvocationMirror::EncodeType(level, kind)));
|
||||
|
||||
// NoSuchMethodError._throwNew takes the following arguments:
|
||||
// Object receiver,
|
||||
// String memberName,
|
||||
// int invocationType,
|
||||
// int typeArgumentsLength,
|
||||
// Object? typeArguments,
|
||||
// List? arguments,
|
||||
// List? argumentNames
|
||||
const Array& args = Array::Handle(Array::New(7));
|
||||
args.SetAt(0, receiver);
|
||||
args.SetAt(1, member_name);
|
||||
args.SetAt(2, invocation_type);
|
||||
args.SetAt(3, Object::smi_zero());
|
||||
args.SetAt(4, Object::null_type_arguments());
|
||||
args.SetAt(5, Object::null_object());
|
||||
args.SetAt(6, Object::null_object());
|
||||
return args.ptr();
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
void CallSiteResetter::RebindBytecode(const Bytecode& bytecode) {
|
||||
#if defined(DART_DYNAMIC_MODULES)
|
||||
pool_ = bytecode.object_pool();
|
||||
@@ -858,22 +911,58 @@ void CallSiteResetter::RebindBytecode(const Bytecode& bytecode) {
|
||||
case KernelBytecode::kUncheckedDirectCall:
|
||||
case KernelBytecode::kUncheckedDirectCall_Wide: {
|
||||
const intptr_t idx = KernelBytecode::DecodeD(instr);
|
||||
old_target_ ^= pool_.ObjectAt(idx);
|
||||
object_ = pool_.ObjectAt(idx);
|
||||
if (object_.IsArray()) {
|
||||
break;
|
||||
}
|
||||
old_target_ ^= object_.ptr();
|
||||
args_desc_array_ ^= pool_.ObjectAt(idx + 1);
|
||||
ArgumentsDescriptor args_desc(args_desc_array_);
|
||||
name_ = old_target_.name();
|
||||
new_cls_ = old_target_.Owner();
|
||||
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
|
||||
if (new_target_.ptr() != old_target_.ptr()) {
|
||||
if (!new_target_.IsNull() &&
|
||||
(new_target_.is_static() == old_target_.is_static()) &&
|
||||
(new_target_.kind() == old_target_.kind()) &&
|
||||
new_target_.AreValidArguments(args_desc, nullptr)) {
|
||||
pool_.SetObjectAt(idx, new_target_);
|
||||
// Re-resolve class in case it was deleted.
|
||||
old_cls_ = old_target_.Owner();
|
||||
old_lib_ = old_cls_.library();
|
||||
name_ = old_lib_.url();
|
||||
new_lib_ = Library::LookupLibrary(thread_, name_);
|
||||
if (!new_lib_.IsNull()) {
|
||||
if (old_cls_.IsTopLevel()) {
|
||||
new_cls_ = new_lib_.toplevel_class();
|
||||
} else {
|
||||
name_ = old_cls_.Name();
|
||||
new_cls_ = new_lib_.LookupClassAllowPrivate(name_);
|
||||
}
|
||||
} else {
|
||||
new_cls_ = Class::null();
|
||||
}
|
||||
if (!new_cls_.IsNull()) {
|
||||
name_ = old_target_.name();
|
||||
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
|
||||
if (new_target_.IsNull() && Field::IsGetterName(name_)) {
|
||||
name_ = Field::NameFromGetter(name_);
|
||||
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
|
||||
if (!new_target_.IsNull()) {
|
||||
name_ = old_target_.name();
|
||||
new_target_ = new_target_.GetMethodExtractor(name_);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new_target_ = Function::null();
|
||||
}
|
||||
if (new_target_.ptr() != old_target_.ptr()) {
|
||||
if (new_target_.IsNull() ||
|
||||
(new_target_.is_static() != old_target_.is_static())) {
|
||||
VTIR_Print("Cannot rebind function %s\n",
|
||||
old_target_.ToFullyQualifiedCString());
|
||||
object_ = PrepareNoSuchMethodErrorArguments(
|
||||
old_target_, /*incompatible_arguments=*/false);
|
||||
} else if (!new_target_.AreValidArguments(args_desc, nullptr)) {
|
||||
VTIR_Print("Cannot rebind function %s - arguments mismatch\n",
|
||||
old_target_.ToFullyQualifiedCString());
|
||||
object_ = PrepareNoSuchMethodErrorArguments(
|
||||
old_target_, /*incompatible_arguments=*/true);
|
||||
} else {
|
||||
object_ = new_target_.ptr();
|
||||
}
|
||||
pool_.SetObjectAt(idx, object_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3345,6 +3345,25 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromPrologue, 4) {
|
||||
arguments.SetReturn(result);
|
||||
}
|
||||
|
||||
// Throw NoSuchMethodError with given arguments.
|
||||
// Arg0: arguments of NoSuchMethodError._throwNew.
|
||||
DEFINE_RUNTIME_ENTRY(NoSuchMethodError, 1) {
|
||||
const Array& args = Array::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const Library& libcore = Library::Handle(Library::CoreLibrary());
|
||||
const Class& cls =
|
||||
Class::Handle(libcore.LookupClass(Symbols::NoSuchMethodError()));
|
||||
ASSERT(!cls.IsNull());
|
||||
const auto& error = cls.EnsureIsFinalized(Thread::Current());
|
||||
ASSERT(error == Error::null());
|
||||
const Function& throwNew =
|
||||
Function::Handle(cls.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
|
||||
ASSERT(args.Length() == throwNew.NumParameters());
|
||||
const Object& result =
|
||||
Object::Handle(zone, DartEntry::InvokeFunction(throwNew, args));
|
||||
ThrowIfError(result);
|
||||
arguments.SetReturn(result);
|
||||
}
|
||||
|
||||
// Invoke appropriate noSuchMethod function (or in the case of no lazy
|
||||
// dispatchers, walk the receiver to find the correct method to call).
|
||||
// Arg0: receiver
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace dart {
|
||||
V(InstantiateTypeArguments) \
|
||||
V(NoSuchMethodFromCallStub) \
|
||||
V(NoSuchMethodFromPrologue) \
|
||||
V(NoSuchMethodError) \
|
||||
V(OptimizeInvokedFunction) \
|
||||
V(PatchStaticCall) \
|
||||
V(RangeError) \
|
||||
|
||||
Reference in New Issue
Block a user