Skip to content

Commit 6082256

Browse files
alexmarkovCommit Queue
authored andcommitted
[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 <[email protected]> Commit-Queue: Alexander Markov <[email protected]>
1 parent 21ce423 commit 6082256

File tree

6 files changed

+1297
-1157
lines changed

6 files changed

+1297
-1157
lines changed

runtime/vm/compiler/runtime_offsets_extracted.h

Lines changed: 1144 additions & 1144 deletions
Large diffs are not rendered by default.

runtime/vm/interpreter.cc

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,7 +2030,15 @@ ObjectPtr Interpreter::Run(Thread* thread,
20302030
const uint32_t kidx = rD;
20312031

20322032
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
2033-
*++SP = LOAD_CONSTANT(kidx);
2033+
ObjectPtr target = LOAD_CONSTANT(kidx);
2034+
*++SP = target;
2035+
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
2036+
if (target->IsArray()) {
2037+
// Hot reload failed to find a suitable target for this call.
2038+
goto ThrowNoSuchMethodError;
2039+
}
2040+
#endif
2041+
ASSERT(target->IsFunction());
20342042
ObjectPtr* call_base = SP - argc;
20352043
ObjectPtr* call_top = SP;
20362044
argdesc_ = static_cast<ArrayPtr>(LOAD_CONSTANT(kidx + 1));
@@ -2051,7 +2059,15 @@ ObjectPtr Interpreter::Run(Thread* thread,
20512059
const uint32_t kidx = rD;
20522060

20532061
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
2054-
*++SP = LOAD_CONSTANT(kidx);
2062+
ObjectPtr target = LOAD_CONSTANT(kidx);
2063+
*++SP = target;
2064+
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
2065+
if (target->IsArray()) {
2066+
// Hot reload failed to find a suitable target for this call.
2067+
goto ThrowNoSuchMethodError;
2068+
}
2069+
#endif
2070+
ASSERT(target->IsFunction());
20552071
ObjectPtr* call_base = SP - argc;
20562072
ObjectPtr* call_top = SP;
20572073
argdesc_ = static_cast<ArrayPtr>(LOAD_CONSTANT(kidx + 1));
@@ -4015,6 +4031,18 @@ ObjectPtr Interpreter::Run(Thread* thread,
40154031
UNREACHABLE();
40164032
}
40174033

4034+
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
4035+
{
4036+
ThrowNoSuchMethodError:
4037+
// SP[0] contains arguments.
4038+
SP[1] = 0; // Unused space for result.
4039+
Exit(thread, FP, SP + 2, pc);
4040+
INVOKE_RUNTIME(DRT_NoSuchMethodError,
4041+
NativeArguments(thread, 1, SP, SP + 1));
4042+
UNREACHABLE();
4043+
}
4044+
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
4045+
40184046
// Exception handling helper. Gets handler FP and PC from the Interpreter
40194047
// where they were stored by Interpreter::Longjmp and proceeds to execute the
40204048
// handler. Corner case: handler PC can be a fake marker that marks entry

runtime/vm/isolate_reload.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,12 +420,15 @@ class CallSiteResetter : public ValueObject {
420420
void RebindBytecode(const Bytecode& bytecode);
421421

422422
private:
423+
Thread* thread_;
423424
Zone* zone_;
424425
Instructions& instrs_;
425426
ObjectPool& pool_;
426427
Object& object_;
427428
String& name_;
429+
Class& old_cls_;
428430
Class& new_cls_;
431+
Library& old_lib_;
429432
Library& new_lib_;
430433
Function& new_function_;
431434
Field& new_field_;

runtime/vm/object_reload.cc

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "vm/object.h"
66

7+
#include "lib/invocation_mirror.h"
78
#include "platform/unaligned.h"
89
#include "vm/code_patcher.h"
910
#include "vm/dart_entry.h"
@@ -41,12 +42,15 @@ void CallSiteResetter::ZeroEdgeCounters(const Function& function) {
4142
}
4243

4344
CallSiteResetter::CallSiteResetter(Zone* zone)
44-
: zone_(zone),
45+
: thread_(Thread::Current()),
46+
zone_(zone),
4547
instrs_(Instructions::Handle(zone)),
4648
pool_(ObjectPool::Handle(zone)),
4749
object_(Object::Handle(zone)),
4850
name_(String::Handle(zone)),
51+
old_cls_(Class::Handle(zone)),
4952
new_cls_(Class::Handle(zone)),
53+
old_lib_(Library::Handle(zone)),
5054
new_lib_(Library::Handle(zone)),
5155
new_function_(Function::Handle(zone)),
5256
new_field_(Field::Handle(zone)),
@@ -840,6 +844,55 @@ void CallSiteResetter::Reset(const ICData& ic) {
840844
}
841845
}
842846

847+
#if defined(DART_DYNAMIC_MODULES)
848+
static ArrayPtr PrepareNoSuchMethodErrorArguments(const Function& target,
849+
bool incompatible_arguments) {
850+
InvocationMirror::Kind kind = InvocationMirror::Kind::kMethod;
851+
if (target.IsImplicitGetterFunction() || target.IsGetterFunction()) {
852+
kind = InvocationMirror::kGetter;
853+
} else if (target.IsImplicitSetterFunction() || target.IsSetterFunction()) {
854+
kind = InvocationMirror::kSetter;
855+
}
856+
const Class& owner = Class::Handle(target.Owner());
857+
auto& receiver = Instance::Handle();
858+
InvocationMirror::Level level;
859+
if (owner.IsTopLevel()) {
860+
if (incompatible_arguments) {
861+
receiver = target.UserVisibleSignature();
862+
}
863+
level = InvocationMirror::Level::kTopLevel;
864+
} else {
865+
receiver = owner.RareType();
866+
if (target.IsConstructor()) {
867+
level = InvocationMirror::Level::kConstructor;
868+
} else {
869+
level = InvocationMirror::Level::kStatic;
870+
}
871+
}
872+
const auto& member_name = String::Handle(target.name());
873+
const auto& invocation_type =
874+
Smi::Handle(Smi::New(InvocationMirror::EncodeType(level, kind)));
875+
876+
// NoSuchMethodError._throwNew takes the following arguments:
877+
// Object receiver,
878+
// String memberName,
879+
// int invocationType,
880+
// int typeArgumentsLength,
881+
// Object? typeArguments,
882+
// List? arguments,
883+
// List? argumentNames
884+
const Array& args = Array::Handle(Array::New(7));
885+
args.SetAt(0, receiver);
886+
args.SetAt(1, member_name);
887+
args.SetAt(2, invocation_type);
888+
args.SetAt(3, Object::smi_zero());
889+
args.SetAt(4, Object::null_type_arguments());
890+
args.SetAt(5, Object::null_object());
891+
args.SetAt(6, Object::null_object());
892+
return args.ptr();
893+
}
894+
#endif // defined(DART_DYNAMIC_MODULES)
895+
843896
void CallSiteResetter::RebindBytecode(const Bytecode& bytecode) {
844897
#if defined(DART_DYNAMIC_MODULES)
845898
pool_ = bytecode.object_pool();
@@ -858,22 +911,58 @@ void CallSiteResetter::RebindBytecode(const Bytecode& bytecode) {
858911
case KernelBytecode::kUncheckedDirectCall:
859912
case KernelBytecode::kUncheckedDirectCall_Wide: {
860913
const intptr_t idx = KernelBytecode::DecodeD(instr);
861-
old_target_ ^= pool_.ObjectAt(idx);
914+
object_ = pool_.ObjectAt(idx);
915+
if (object_.IsArray()) {
916+
break;
917+
}
918+
old_target_ ^= object_.ptr();
862919
args_desc_array_ ^= pool_.ObjectAt(idx + 1);
863920
ArgumentsDescriptor args_desc(args_desc_array_);
864-
name_ = old_target_.name();
865-
new_cls_ = old_target_.Owner();
866-
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
867-
if (new_target_.ptr() != old_target_.ptr()) {
868-
if (!new_target_.IsNull() &&
869-
(new_target_.is_static() == old_target_.is_static()) &&
870-
(new_target_.kind() == old_target_.kind()) &&
871-
new_target_.AreValidArguments(args_desc, nullptr)) {
872-
pool_.SetObjectAt(idx, new_target_);
921+
// Re-resolve class in case it was deleted.
922+
old_cls_ = old_target_.Owner();
923+
old_lib_ = old_cls_.library();
924+
name_ = old_lib_.url();
925+
new_lib_ = Library::LookupLibrary(thread_, name_);
926+
if (!new_lib_.IsNull()) {
927+
if (old_cls_.IsTopLevel()) {
928+
new_cls_ = new_lib_.toplevel_class();
873929
} else {
930+
name_ = old_cls_.Name();
931+
new_cls_ = new_lib_.LookupClassAllowPrivate(name_);
932+
}
933+
} else {
934+
new_cls_ = Class::null();
935+
}
936+
if (!new_cls_.IsNull()) {
937+
name_ = old_target_.name();
938+
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
939+
if (new_target_.IsNull() && Field::IsGetterName(name_)) {
940+
name_ = Field::NameFromGetter(name_);
941+
new_target_ = Resolver::ResolveFunction(zone_, new_cls_, name_);
942+
if (!new_target_.IsNull()) {
943+
name_ = old_target_.name();
944+
new_target_ = new_target_.GetMethodExtractor(name_);
945+
}
946+
}
947+
} else {
948+
new_target_ = Function::null();
949+
}
950+
if (new_target_.ptr() != old_target_.ptr()) {
951+
if (new_target_.IsNull() ||
952+
(new_target_.is_static() != old_target_.is_static())) {
874953
VTIR_Print("Cannot rebind function %s\n",
875954
old_target_.ToFullyQualifiedCString());
955+
object_ = PrepareNoSuchMethodErrorArguments(
956+
old_target_, /*incompatible_arguments=*/false);
957+
} else if (!new_target_.AreValidArguments(args_desc, nullptr)) {
958+
VTIR_Print("Cannot rebind function %s - arguments mismatch\n",
959+
old_target_.ToFullyQualifiedCString());
960+
object_ = PrepareNoSuchMethodErrorArguments(
961+
old_target_, /*incompatible_arguments=*/true);
962+
} else {
963+
object_ = new_target_.ptr();
876964
}
965+
pool_.SetObjectAt(idx, object_);
877966
}
878967
break;
879968
}

runtime/vm/runtime_entry.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3345,6 +3345,25 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromPrologue, 4) {
33453345
arguments.SetReturn(result);
33463346
}
33473347

3348+
// Throw NoSuchMethodError with given arguments.
3349+
// Arg0: arguments of NoSuchMethodError._throwNew.
3350+
DEFINE_RUNTIME_ENTRY(NoSuchMethodError, 1) {
3351+
const Array& args = Array::CheckedHandle(zone, arguments.ArgAt(0));
3352+
const Library& libcore = Library::Handle(Library::CoreLibrary());
3353+
const Class& cls =
3354+
Class::Handle(libcore.LookupClass(Symbols::NoSuchMethodError()));
3355+
ASSERT(!cls.IsNull());
3356+
const auto& error = cls.EnsureIsFinalized(Thread::Current());
3357+
ASSERT(error == Error::null());
3358+
const Function& throwNew =
3359+
Function::Handle(cls.LookupFunctionAllowPrivate(Symbols::ThrowNew()));
3360+
ASSERT(args.Length() == throwNew.NumParameters());
3361+
const Object& result =
3362+
Object::Handle(zone, DartEntry::InvokeFunction(throwNew, args));
3363+
ThrowIfError(result);
3364+
arguments.SetReturn(result);
3365+
}
3366+
33483367
// Invoke appropriate noSuchMethod function (or in the case of no lazy
33493368
// dispatchers, walk the receiver to find the correct method to call).
33503369
// Arg0: receiver

runtime/vm/runtime_entry_list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace dart {
4343
V(InstantiateTypeArguments) \
4444
V(NoSuchMethodFromCallStub) \
4545
V(NoSuchMethodFromPrologue) \
46+
V(NoSuchMethodError) \
4647
V(OptimizeInvokedFunction) \
4748
V(PatchStaticCall) \
4849
V(RangeError) \

0 commit comments

Comments
 (0)