mirror of
https://github.com/dart-lang/sdk
synced 2024-10-15 00:10:36 +00:00
587e6308bf
When evaluating an expressino at a breakpoint that's inside a closure, the closure may refer to anything it closes over. That includes the `this` from an enclosing method. So depending on whether a closure's parent chain is an instance method or a static method, the expression evaluation function is going to be an instance method or a static method. => We walk up the parent chain to determine the correct enclosing class. => This avoids making a Type object with a cid from a top-level class (see [0]) Handling this correctly will now try to get the `this` when invoking an eval function that has an enclosing instance method. Though we may often have "<optimized out>" the `this` (e.g. due to not capturing it in closure context chain). => We still allow running the expression evaluation function in this case, but only if the expression being evaluated doesn't access `this`. A similar issue occurs when trying to use variables in the eval expression that the closure didn't capture. This results in a confusing CFE compile-time error. This is a separate issue and tracked in [1]. => We update the test to distinuish the cases that this CL makes passing and those that are failing due to [1]. Fixes [0] https://github.com/dart-lang/sdk/issues/53061 See also [1] https://github.com/dart-lang/sdk/issues/53087 TEST=Fixes part of service/evaluate_activation_test failures Change-Id: I3bb24e7338c7b2f12d5340311d944cb59a455641 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/317540 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
353 lines
11 KiB
C++
353 lines
11 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_PARSER_H_
|
|
#define RUNTIME_VM_PARSER_H_
|
|
|
|
#include "include/dart_api.h"
|
|
|
|
#include "lib/invocation_mirror.h"
|
|
#include "platform/assert.h"
|
|
#include "platform/globals.h"
|
|
#include "vm/allocation.h"
|
|
#include "vm/class_finalizer.h"
|
|
#include "vm/hash_table.h"
|
|
#include "vm/kernel.h"
|
|
#include "vm/object.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/scopes.h"
|
|
#include "vm/token.h"
|
|
|
|
namespace dart {
|
|
|
|
// Forward declarations.
|
|
|
|
namespace kernel {
|
|
|
|
class ScopeBuildingResult;
|
|
|
|
} // namespace kernel
|
|
|
|
class ArgumentsDescriptor;
|
|
class BitVector;
|
|
class Isolate;
|
|
class LocalScope;
|
|
class LocalVariable;
|
|
struct RegExpCompileData;
|
|
template <typename T>
|
|
class GrowableArray;
|
|
class Parser;
|
|
|
|
class FieldKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const Field* Key;
|
|
typedef const Field* Value;
|
|
typedef const Field* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline uword Hash(Key key) {
|
|
const TokenPosition token_pos = key->token_pos();
|
|
if (token_pos.IsReal()) {
|
|
return token_pos.Hash();
|
|
}
|
|
return key->kernel_offset();
|
|
}
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
return pair->Original() == key->Original();
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<FieldKeyValueTrait> FieldSet;
|
|
|
|
// The class ParsedFunction holds the result of parsing a function.
|
|
class ParsedFunction : public ZoneAllocated {
|
|
public:
|
|
ParsedFunction(Thread* thread, const Function& function);
|
|
|
|
const Function& function() const { return function_; }
|
|
const Code& code() const { return code_; }
|
|
|
|
LocalScope* scope() const { return scope_; }
|
|
void set_scope(LocalScope* scope) {
|
|
ASSERT(scope_ == nullptr);
|
|
ASSERT(scope != nullptr);
|
|
scope_ = scope;
|
|
}
|
|
|
|
RegExpCompileData* regexp_compile_data() const {
|
|
return regexp_compile_data_;
|
|
}
|
|
void SetRegExpCompileData(RegExpCompileData* regexp_compile_data);
|
|
|
|
LocalVariable* function_type_arguments() const {
|
|
return function_type_arguments_;
|
|
}
|
|
void set_function_type_arguments(LocalVariable* function_type_arguments) {
|
|
ASSERT(function_type_arguments != nullptr);
|
|
function_type_arguments_ = function_type_arguments;
|
|
}
|
|
LocalVariable* parent_type_arguments() const {
|
|
return parent_type_arguments_;
|
|
}
|
|
void set_parent_type_arguments(LocalVariable* parent_type_arguments) {
|
|
ASSERT(parent_type_arguments != nullptr);
|
|
parent_type_arguments_ = parent_type_arguments;
|
|
}
|
|
|
|
LocalVariable* suspend_state_var() const { return suspend_state_var_; }
|
|
void set_suspend_state_var(LocalVariable* suspend_state_var) {
|
|
ASSERT(suspend_state_var != nullptr);
|
|
suspend_state_var_ = suspend_state_var;
|
|
}
|
|
|
|
void set_default_parameter_values(ZoneGrowableArray<const Instance*>* list) {
|
|
default_parameter_values_ = list;
|
|
#if defined(DEBUG)
|
|
if (list == nullptr) return;
|
|
for (intptr_t i = 0; i < list->length(); i++) {
|
|
DEBUG_ASSERT(list->At(i)->IsNotTemporaryScopedHandle());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const Instance& DefaultParameterValueAt(intptr_t i) const {
|
|
ASSERT(default_parameter_values_ != nullptr);
|
|
return *default_parameter_values_->At(i);
|
|
}
|
|
|
|
ZoneGrowableArray<const Instance*>* default_parameter_values() const {
|
|
return default_parameter_values_;
|
|
}
|
|
|
|
LocalVariable* current_context_var() const { return current_context_var_; }
|
|
|
|
bool has_arg_desc_var() const { return arg_desc_var_ != nullptr; }
|
|
LocalVariable* arg_desc_var() const { return arg_desc_var_; }
|
|
|
|
LocalVariable* receiver_var() const {
|
|
ASSERT(receiver_var_ != nullptr);
|
|
return receiver_var_;
|
|
}
|
|
void set_receiver_var(LocalVariable* value) {
|
|
ASSERT(receiver_var_ == nullptr);
|
|
ASSERT(value != nullptr);
|
|
receiver_var_ = value;
|
|
}
|
|
bool has_receiver_var() const { return receiver_var_ != nullptr; }
|
|
|
|
void set_receiver_used() { receiver_used_ = true; }
|
|
bool is_receiver_used() const {
|
|
ASSERT(kernel_scopes_ != nullptr);
|
|
ASSERT(!receiver_used_ || receiver_var() != nullptr);
|
|
return receiver_used_;
|
|
}
|
|
|
|
LocalVariable* expression_temp_var() const {
|
|
ASSERT(has_expression_temp_var());
|
|
return expression_temp_var_;
|
|
}
|
|
void set_expression_temp_var(LocalVariable* value) {
|
|
ASSERT(!has_expression_temp_var());
|
|
expression_temp_var_ = value;
|
|
}
|
|
bool has_expression_temp_var() const {
|
|
return expression_temp_var_ != nullptr;
|
|
}
|
|
|
|
LocalVariable* entry_points_temp_var() const {
|
|
ASSERT(has_entry_points_temp_var());
|
|
return entry_points_temp_var_;
|
|
}
|
|
void set_entry_points_temp_var(LocalVariable* value) {
|
|
ASSERT(!has_entry_points_temp_var());
|
|
entry_points_temp_var_ = value;
|
|
}
|
|
bool has_entry_points_temp_var() const {
|
|
return entry_points_temp_var_ != nullptr;
|
|
}
|
|
|
|
LocalVariable* finally_return_temp_var() const {
|
|
ASSERT(has_finally_return_temp_var());
|
|
return finally_return_temp_var_;
|
|
}
|
|
void set_finally_return_temp_var(LocalVariable* value) {
|
|
ASSERT(!has_finally_return_temp_var());
|
|
finally_return_temp_var_ = value;
|
|
}
|
|
bool has_finally_return_temp_var() const {
|
|
return finally_return_temp_var_ != nullptr;
|
|
}
|
|
void EnsureFinallyReturnTemp(bool is_async);
|
|
|
|
LocalVariable* EnsureExpressionTemp();
|
|
LocalVariable* EnsureEntryPointsTemp();
|
|
|
|
const FieldSet* guarded_fields() const { return &guarded_fields_; }
|
|
|
|
VariableIndex first_parameter_index() const { return first_parameter_index_; }
|
|
int num_stack_locals() const { return num_stack_locals_; }
|
|
|
|
void AllocateVariables();
|
|
void AllocateIrregexpVariables(intptr_t num_stack_locals);
|
|
|
|
void record_await() { have_seen_await_expr_ = true; }
|
|
bool have_seen_await() const { return have_seen_await_expr_; }
|
|
bool is_forwarding_stub() const {
|
|
return forwarding_stub_super_target_ != nullptr;
|
|
}
|
|
const Function* forwarding_stub_super_target() const {
|
|
return forwarding_stub_super_target_;
|
|
}
|
|
void MarkForwardingStub(const Function* forwarding_target) {
|
|
forwarding_stub_super_target_ = forwarding_target;
|
|
}
|
|
|
|
Thread* thread() const { return thread_; }
|
|
Isolate* isolate() const { return thread_->isolate(); }
|
|
Zone* zone() const { return thread_->zone(); }
|
|
|
|
// Adds only relevant fields: field must be unique and its guarded_cid()
|
|
// relevant.
|
|
void AddToGuardedFields(const Field* field) const;
|
|
|
|
void Bailout(const char* origin, const char* reason) const;
|
|
|
|
kernel::ScopeBuildingResult* EnsureKernelScopes();
|
|
|
|
LocalVariable* RawTypeArgumentsVariable() const {
|
|
return raw_type_arguments_var_;
|
|
}
|
|
|
|
void SetRawTypeArgumentsVariable(LocalVariable* raw_type_arguments_var) {
|
|
raw_type_arguments_var_ = raw_type_arguments_var;
|
|
}
|
|
|
|
void SetRawParameters(ZoneGrowableArray<LocalVariable*>* raw_parameters) {
|
|
raw_parameters_ = raw_parameters;
|
|
}
|
|
|
|
LocalVariable* RawParameterVariable(intptr_t i) const {
|
|
return raw_parameters_->At(i);
|
|
}
|
|
|
|
LocalVariable* ParameterVariable(intptr_t i) const {
|
|
ASSERT((i >= 0) && (i < function_.NumParameters()));
|
|
ASSERT(scope() != nullptr);
|
|
return scope()->VariableAt(i);
|
|
}
|
|
|
|
// Remembers the set of covariant parameters.
|
|
// [covariant_parameters] is a bitvector of function.NumParameters() length.
|
|
void SetCovariantParameters(const BitVector* covariant_parameters);
|
|
|
|
// Remembers the set of generic-covariant-impl parameters.
|
|
// [covariant_parameters] is a bitvector of function.NumParameters() length.
|
|
void SetGenericCovariantImplParameters(
|
|
const BitVector* generic_covariant_impl_parameters);
|
|
|
|
bool HasCovariantParametersInfo() const {
|
|
return covariant_parameters_ != nullptr;
|
|
}
|
|
|
|
// Returns true if i-th parameter is covariant.
|
|
// SetCovariantParameters should be called before using this method.
|
|
bool IsCovariantParameter(intptr_t i) const;
|
|
|
|
// Returns true if i-th parameter is generic-covariant-impl.
|
|
// SetGenericCovariantImplParameters should be called before using this
|
|
// method.
|
|
bool IsGenericCovariantImplParameter(intptr_t i) const;
|
|
|
|
// Variables needed for the InvokeFieldDispatcher for dynamic closure calls,
|
|
// because they are both read and written to by the builders.
|
|
struct DynamicClosureCallVars : ZoneAllocated {
|
|
DynamicClosureCallVars(Zone* zone, intptr_t num_named)
|
|
: named_argument_parameter_indices(zone, num_named) {}
|
|
|
|
#define FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(V) \
|
|
V(current_function, Function, CurrentFunction) \
|
|
V(current_num_processed, Smi, CurrentNumProcessed) \
|
|
V(current_param_index, Smi, CurrentParamIndex) \
|
|
V(current_type_param, Dynamic, CurrentTypeParam) \
|
|
V(function_type_args, Dynamic, FunctionTypeArgs)
|
|
|
|
#define DEFINE_FIELD(Name, _, __) LocalVariable* Name = nullptr;
|
|
FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(DEFINE_FIELD)
|
|
#undef DEFINE_FIELD
|
|
|
|
// An array of local variables, one for each named parameter in the
|
|
// saved arguments descriptor.
|
|
ZoneGrowableArray<LocalVariable*> named_argument_parameter_indices;
|
|
};
|
|
|
|
DynamicClosureCallVars* dynamic_closure_call_vars() const {
|
|
return dynamic_closure_call_vars_;
|
|
}
|
|
DynamicClosureCallVars* EnsureDynamicClosureCallVars();
|
|
|
|
private:
|
|
Thread* thread_;
|
|
const Function& function_;
|
|
Code& code_;
|
|
LocalScope* scope_;
|
|
RegExpCompileData* regexp_compile_data_;
|
|
LocalVariable* function_type_arguments_;
|
|
LocalVariable* parent_type_arguments_;
|
|
LocalVariable* suspend_state_var_ = nullptr;
|
|
LocalVariable* current_context_var_;
|
|
LocalVariable* arg_desc_var_;
|
|
LocalVariable* receiver_var_ = nullptr;
|
|
LocalVariable* expression_temp_var_;
|
|
LocalVariable* entry_points_temp_var_;
|
|
LocalVariable* finally_return_temp_var_;
|
|
DynamicClosureCallVars* dynamic_closure_call_vars_;
|
|
mutable FieldSet guarded_fields_;
|
|
ZoneGrowableArray<const Instance*>* default_parameter_values_;
|
|
bool receiver_used_ = false;
|
|
|
|
LocalVariable* raw_type_arguments_var_;
|
|
ZoneGrowableArray<LocalVariable*>* raw_parameters_ = nullptr;
|
|
|
|
VariableIndex first_parameter_index_;
|
|
int num_stack_locals_;
|
|
bool have_seen_await_expr_;
|
|
|
|
const Function* forwarding_stub_super_target_ = nullptr;
|
|
kernel::ScopeBuildingResult* kernel_scopes_;
|
|
|
|
const BitVector* covariant_parameters_ = nullptr;
|
|
const BitVector* generic_covariant_impl_parameters_ = nullptr;
|
|
|
|
friend class Parser;
|
|
DISALLOW_COPY_AND_ASSIGN(ParsedFunction);
|
|
};
|
|
|
|
class Parser : public ValueObject {
|
|
public:
|
|
// Parse a function to retrieve parameter information that is not retained in
|
|
// the Function object. Returns either an error if the parse fails (which
|
|
// could be the case for local functions), or a flat array of entries for each
|
|
// parameter. Each parameter entry contains: * a Dart bool indicating whether
|
|
// the parameter was declared final * its default value (or null if none was
|
|
// declared) * an array of metadata (or null if no metadata was declared).
|
|
enum {
|
|
kParameterIsFinalOffset,
|
|
kParameterDefaultValueOffset,
|
|
kParameterMetadataOffset,
|
|
kParameterEntrySize,
|
|
};
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(Parser);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_PARSER_H_
|