52cfd29cbb
The RecordCoverage instruction has an A/E encoding. The A argument is the type of coverage being recorded, whereas the E argument is the logical index into the coverage array for updating whether that source position has been hit. Also adds new metadata to the bytecode component for the coverage arrays associated with bytecode containing RecordCoverage instructions and a new runtime entry for lazily allocate the coverage array for an interpreted function when needed. The type of coverage is encoded in the RecordCoverage instruction, despite being redundant with the information in the coverage array, so that checking whether that type of coverage is currently enabled at runtime doesn't require either accessing the coverage array (which may be lazily allocated), forcing allocation of the coverage array just to discover that type of coverage is currently disabled, or reading the serialized bytecode component to avoid that forced allocation. ------ Other changes: Source reporting now treats unexecuted interpreted functions when not forcing compilation as if they were uncompiled native functions, so that the source report from running the same code gives the same result whether using the interpreter or the native compiler. Bytecode closures are no longer skipped in source reports. Previously any closure without a context scope was skipped, but bytecode closures don't have those. TEST=vm/cc/SourceReport_Coverage Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try Change-Id: I7557e5dd4c98331c7ca2f5c867dd5f6d03e9d756 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501520 Reviewed-by: Alexander Markov <alexmarkov@google.com>
778 lines
26 KiB
C++
778 lines
26 KiB
C++
// Copyright (c) 2024, 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_BYTECODE_READER_H_
|
|
#define RUNTIME_VM_BYTECODE_READER_H_
|
|
|
|
#include "vm/globals.h"
|
|
#if defined(DART_DYNAMIC_MODULES)
|
|
|
|
#include "vm/bit_vector.h"
|
|
#include "vm/constants_kbc.h"
|
|
#include "vm/hash_table.h"
|
|
#include "vm/object.h"
|
|
|
|
namespace dart {
|
|
namespace bytecode {
|
|
|
|
class BytecodeComponentData;
|
|
|
|
class BytecodeLoader {
|
|
public:
|
|
BytecodeLoader(Thread* thread, const TypedDataBase& binary);
|
|
~BytecodeLoader();
|
|
|
|
FunctionPtr LoadBytecode(bool load_code = true);
|
|
void LoadPendingCode();
|
|
|
|
TypedDataBasePtr binary() const { return binary_.ptr(); }
|
|
ArrayPtr bytecode_component_array() const {
|
|
return bytecode_component_array_.ptr();
|
|
}
|
|
|
|
void AddPendingObject(const Object& obj, intptr_t offset) {
|
|
SetOffset(obj, offset);
|
|
pending_objects_.Add(obj);
|
|
}
|
|
|
|
void SetOffset(const Object& obj, intptr_t offset);
|
|
intptr_t GetOffset(const Object& obj) const;
|
|
bool HasOffset(const Object& obj) const;
|
|
|
|
void FindModifiedLibraries(BitVector* modified_libs,
|
|
intptr_t* p_num_libraries,
|
|
intptr_t* p_num_classes,
|
|
intptr_t* p_num_procedures);
|
|
|
|
void SetExpressionEvaluationLibrary(const Library& lib) {
|
|
ASSERT(expression_evaluation_library_ == nullptr);
|
|
expression_evaluation_library_ =
|
|
&Library::ZoneHandle(thread_->zone(), lib.ptr());
|
|
}
|
|
LibraryPtr GetExpressionEvaluationLibrary() const {
|
|
ASSERT(expression_evaluation_library_ != nullptr);
|
|
return expression_evaluation_library_->ptr();
|
|
}
|
|
void SetExpressionEvaluationRealClass(const Class& cls) {
|
|
ASSERT(expression_evaluation_real_classs_ == nullptr);
|
|
expression_evaluation_real_classs_ =
|
|
&Class::ZoneHandle(thread_->zone(), cls.ptr());
|
|
}
|
|
ClassPtr GetExpressionEvaluationRealClass() const {
|
|
ASSERT(expression_evaluation_real_classs_ != nullptr);
|
|
return expression_evaluation_real_classs_->ptr();
|
|
}
|
|
void SetExpressionEvaluationFunction(const Function& func) {
|
|
ASSERT(expression_evaluation_function_ == nullptr);
|
|
expression_evaluation_function_ =
|
|
&Function::ZoneHandle(thread_->zone(), func.ptr());
|
|
}
|
|
FunctionPtr GetExpressionEvaluationFunction() const {
|
|
ASSERT(expression_evaluation_function_ != nullptr);
|
|
return expression_evaluation_function_->ptr();
|
|
}
|
|
|
|
private:
|
|
Thread* thread_;
|
|
const TypedDataBase& binary_;
|
|
Array& bytecode_component_array_;
|
|
const GrowableObjectArray& pending_objects_;
|
|
Array& bytecode_offsets_map_;
|
|
Library* expression_evaluation_library_ = nullptr;
|
|
Class* expression_evaluation_real_classs_ = nullptr;
|
|
Function* expression_evaluation_function_ = nullptr;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(BytecodeLoader);
|
|
};
|
|
|
|
class Reader : public ValueObject {
|
|
public:
|
|
explicit Reader(const TypedDataBase& typed_data) : typed_data_(&typed_data) {
|
|
Init();
|
|
}
|
|
|
|
uint32_t ReadUInt32At(intptr_t offset) const {
|
|
ASSERT((size_ >= 4) && (offset >= 0) && (offset <= size_ - 4));
|
|
uint32_t value =
|
|
LoadUnaligned(reinterpret_cast<const uint32_t*>(raw_buffer_ + offset));
|
|
// All supported platforms are little-endian, so there is no need to
|
|
// convert from little-endian to host.
|
|
return value;
|
|
}
|
|
|
|
uint32_t ReadUInt32() {
|
|
uint32_t value = ReadUInt32At(offset_);
|
|
offset_ += 4;
|
|
return value;
|
|
}
|
|
|
|
uint32_t ReadUInt() {
|
|
ASSERT((size_ >= 1) && (offset_ >= 0) && (offset_ <= size_ - 1));
|
|
|
|
const uint8_t* buffer = raw_buffer_;
|
|
uword byte0 = buffer[offset_];
|
|
if ((byte0 & 0x80) == 0) {
|
|
// 0...
|
|
offset_++;
|
|
return byte0;
|
|
} else if ((byte0 & 0xc0) == 0x80) {
|
|
// 10...
|
|
ASSERT((size_ >= 2) && (offset_ >= 0) && (offset_ <= size_ - 2));
|
|
uint32_t value =
|
|
((byte0 & ~static_cast<uword>(0x80)) << 8) | (buffer[offset_ + 1]);
|
|
offset_ += 2;
|
|
return value;
|
|
} else {
|
|
// 11...
|
|
ASSERT((size_ >= 4) && (offset_ >= 0) && (offset_ <= size_ - 4));
|
|
uint32_t value = ((byte0 & ~static_cast<uword>(0xc0)) << 24) |
|
|
(buffer[offset_ + 1] << 16) |
|
|
(buffer[offset_ + 2] << 8) | (buffer[offset_ + 3] << 0);
|
|
offset_ += 4;
|
|
return value;
|
|
}
|
|
}
|
|
|
|
intptr_t ReadSLEB128() {
|
|
ReadStream stream(raw_buffer_, size_, offset_);
|
|
const intptr_t result = stream.ReadSLEB128();
|
|
offset_ = stream.Position();
|
|
return result;
|
|
}
|
|
|
|
int64_t ReadSLEB128AsInt64() {
|
|
ReadStream stream(raw_buffer_, size_, offset_);
|
|
const int64_t result = stream.ReadSLEB128<int64_t>();
|
|
offset_ = stream.Position();
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Read and return a TokenPosition from this reader.
|
|
*/
|
|
TokenPosition ReadPosition() {
|
|
// Position is saved as unsigned,
|
|
// but actually ranges from -1 and up (thus the -1)
|
|
intptr_t value = ReadUInt() - 1;
|
|
TokenPosition result = TokenPosition::Deserialize(value);
|
|
return result;
|
|
}
|
|
|
|
intptr_t ReadListLength() { return ReadUInt(); }
|
|
|
|
uint8_t ReadByte() { return raw_buffer_[offset_++]; }
|
|
|
|
uint8_t PeekByte() { return raw_buffer_[offset_]; }
|
|
|
|
void ReadBytes(uint8_t* buffer, uint8_t size) {
|
|
for (int i = 0; i < size; i++) {
|
|
buffer[i] = ReadByte();
|
|
}
|
|
}
|
|
|
|
const TypedDataBase* typed_data() { return typed_data_; }
|
|
|
|
intptr_t offset() const { return offset_; }
|
|
void set_offset(intptr_t offset) {
|
|
ASSERT(offset <= size_);
|
|
offset_ = offset;
|
|
}
|
|
intptr_t size() const { return size_; }
|
|
|
|
TypedDataViewPtr ViewFromTo(intptr_t start, intptr_t end) {
|
|
return typed_data_->ViewFromTo(start, end, Heap::kOld);
|
|
}
|
|
|
|
const uint8_t* BufferAt(intptr_t offset) {
|
|
ASSERT((offset >= 0) && (offset < size_));
|
|
return &raw_buffer_[offset];
|
|
}
|
|
|
|
private:
|
|
friend class AlternativeReadingScope;
|
|
|
|
void Init() {
|
|
ASSERT(typed_data_->IsExternalOrExternalView());
|
|
raw_buffer_ = reinterpret_cast<uint8_t*>(typed_data_->DataAddr(0));
|
|
size_ = typed_data_->LengthInBytes();
|
|
offset_ = 0;
|
|
}
|
|
|
|
// A external typed data or a view on an external typed data.
|
|
const TypedDataBase* typed_data_ = nullptr;
|
|
|
|
// The raw data size/length of [typed_data_].
|
|
const uint8_t* raw_buffer_ = nullptr;
|
|
intptr_t size_ = 0;
|
|
|
|
intptr_t offset_ = 0;
|
|
};
|
|
|
|
// A helper class that saves the current reader position, goes to another reader
|
|
// position, and upon destruction, resets to the original reader position.
|
|
class AlternativeReadingScope {
|
|
public:
|
|
AlternativeReadingScope(Reader* reader, intptr_t new_position)
|
|
: reader_(reader), saved_offset_(reader_->offset_) {
|
|
reader_->offset_ = new_position;
|
|
}
|
|
|
|
~AlternativeReadingScope() { reader_->offset_ = saved_offset_; }
|
|
|
|
private:
|
|
Reader* const reader_;
|
|
const intptr_t saved_offset_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AlternativeReadingScope);
|
|
};
|
|
|
|
// Helper class for reading bytecode.
|
|
class BytecodeReaderHelper : public ValueObject {
|
|
public:
|
|
explicit BytecodeReaderHelper(Thread* thread,
|
|
const TypedDataBase& typed_data);
|
|
|
|
explicit BytecodeReaderHelper(Thread* thread,
|
|
BytecodeComponentData* bytecode_component);
|
|
|
|
Reader& reader() { return reader_; }
|
|
|
|
void ReadCode(const Function& function, intptr_t code_offset);
|
|
void ReadCoveredConstConstructors(const Script& script, intptr_t offset);
|
|
|
|
void ReadMembers(const Class& cls, bool discard_fields);
|
|
|
|
void ReadFieldDeclarations(const Class& cls, bool discard_fields);
|
|
void ReadFunctionDeclarations(const Class& cls);
|
|
void ReadClassDeclaration(const Class& cls);
|
|
void ReadLibraryDeclaration(const Library& library, bool register_classes);
|
|
void ReadLibraryDeclarations(intptr_t num_libraries,
|
|
const GrowableObjectArray& pending_objects,
|
|
bool load_code);
|
|
void ReadPendingCode(const GrowableObjectArray& pending_objects);
|
|
void FindModifiedLibraries(BitVector* modified_libs, intptr_t num_libraries);
|
|
|
|
LibraryPtr ReadMain();
|
|
|
|
ArrayPtr ReadBytecodeComponent();
|
|
void ResetObjects();
|
|
|
|
// Fills in [is_covariant] and [is_generic_covariant_impl] vectors
|
|
// according to covariance attributes of [function] parameters.
|
|
//
|
|
// [function] should be declared in bytecode.
|
|
// [is_covariant] and [is_generic_covariant_impl] should contain bitvectors
|
|
// of function.NumParameters() length.
|
|
void ReadParameterCovariance(const Function& function,
|
|
intptr_t code_offset,
|
|
BitVector* is_covariant,
|
|
BitVector* is_generic_covariant_impl);
|
|
|
|
// Read bytecode PackedObject.
|
|
ObjectPtr ReadObject();
|
|
|
|
private:
|
|
// These constants should match corresponding constants in class ObjectHandle
|
|
// (pkg/dart2bytecode/lib/object_table.dart).
|
|
static const int kReferenceBit = 1 << 0;
|
|
static const int kIndexShift = 1;
|
|
static const int kKindShift = 1;
|
|
static const int kKindMask = 0x0f;
|
|
static const int kFlagBit0 = 1 << 5;
|
|
static const int kFlagBit1 = 1 << 6;
|
|
static const int kFlagBit2 = 1 << 7;
|
|
static const int kFlagBit3 = 1 << 8;
|
|
static const int kFlagBit4 = 1 << 9;
|
|
static const int kFlagBit5 = 1 << 10;
|
|
static const int kTagMask = (kFlagBit0 | kFlagBit1 | kFlagBit2 | kFlagBit3);
|
|
static const int kFlagIsNullable = kFlagBit4;
|
|
static const int kFlagsMask = (kTagMask | kFlagBit4 | kFlagBit5);
|
|
|
|
// Code flags, must be in sync with Code constants in
|
|
// pkg/dart2bytecode/lib/declarations.dart.
|
|
struct Code {
|
|
static const int kHasExceptionsTableFlag = 1 << 0;
|
|
static const int kHasSourcePositionsFlag = 1 << 1;
|
|
static const int kHasNullableFieldsFlag = 1 << 2;
|
|
static const int kHasClosuresFlag = 1 << 3;
|
|
static const int kHasParameterFlagsFlag = 1 << 4;
|
|
static const int kHasForwardingStubTargetFlag = 1 << 5;
|
|
static const int kHasDefaultFunctionTypeArgsFlag = 1 << 6;
|
|
static const int kHasLocalVariablesFlag = 1 << 7;
|
|
static const int kHasRecordedCoverageFlag = 1 << 8;
|
|
};
|
|
|
|
// Closure code flags, must be in sync with ClosureCode constants in
|
|
// pkg/dart2bytecode/lib/declarations.dart.
|
|
struct ClosureCode {
|
|
static const int kHasExceptionsTableFlag = 1 << 0;
|
|
static const int kHasSourcePositionsFlag = 1 << 1;
|
|
static const int kHasLocalVariablesFlag = 1 << 2;
|
|
static const int kCapturesOnlyFinalNotLateVarsFlag = 1 << 3;
|
|
static const int kHasLocalFunctionIdFlag = 1 << 4;
|
|
static const int kHasRecordedCoverageFlag = 1 << 5;
|
|
};
|
|
|
|
// Parameter flags, must be in sync with ParameterFlags constants in
|
|
// pkg/dart2bytecode/lib/object_table.dart.
|
|
struct Parameter {
|
|
// Parameter flags in FunctionDeclaration, ClosureDeclaration and
|
|
// FunctionType.
|
|
static const int kIsRequiredFlag = 1 << 0;
|
|
// Parameter flags in Code.
|
|
static const int kIsCovariantFlag = 1 << 0;
|
|
static const int kIsCovariantByClassFlag = 1 << 1;
|
|
};
|
|
|
|
class FunctionTypeScope : public ValueObject {
|
|
public:
|
|
explicit FunctionTypeScope(BytecodeReaderHelper* bytecode_reader,
|
|
const FunctionType& type)
|
|
: bytecode_reader_(bytecode_reader) {
|
|
bytecode_reader_->enclosing_function_types_.Add(&type);
|
|
}
|
|
|
|
~FunctionTypeScope() {
|
|
bytecode_reader_->enclosing_function_types_.RemoveLast();
|
|
}
|
|
|
|
private:
|
|
BytecodeReaderHelper* const bytecode_reader_;
|
|
};
|
|
|
|
class FunctionScope : public ValueObject {
|
|
public:
|
|
FunctionScope(BytecodeReaderHelper* bytecode_reader,
|
|
const Function& function,
|
|
const String& name,
|
|
const Class& cls)
|
|
: bytecode_reader_(bytecode_reader) {
|
|
ASSERT(bytecode_reader_->scoped_function_.IsNull());
|
|
ASSERT(bytecode_reader_->scoped_function_name_.IsNull());
|
|
ASSERT(bytecode_reader_->scoped_function_class_.IsNull());
|
|
ASSERT(name.IsSymbol());
|
|
bytecode_reader_->scoped_function_ = function.ptr();
|
|
bytecode_reader_->scoped_function_name_ = name.ptr();
|
|
bytecode_reader_->scoped_function_class_ = cls.ptr();
|
|
}
|
|
|
|
~FunctionScope() {
|
|
bytecode_reader_->scoped_function_ = Function::null();
|
|
bytecode_reader_->scoped_function_name_ = String::null();
|
|
bytecode_reader_->scoped_function_class_ = Class::null();
|
|
}
|
|
|
|
private:
|
|
BytecodeReaderHelper* bytecode_reader_;
|
|
};
|
|
|
|
void ReadClosureDeclaration(const Function& function, intptr_t closureIndex);
|
|
FunctionTypePtr ReadFunctionSignature(const FunctionType& signature,
|
|
const Function& closure_function,
|
|
bool has_optional_positional_params,
|
|
bool has_optional_named_params,
|
|
bool has_type_params,
|
|
bool has_positional_param_names,
|
|
bool has_parameter_flags);
|
|
void ReadTypeParametersDeclaration(
|
|
const Class& parameterized_class,
|
|
const FunctionType& parameterized_signature);
|
|
|
|
// Read portion of constant pool corresponding to one function/closure.
|
|
// Start with [start_index], and stop when reaching EndClosureFunctionScope.
|
|
// Return index of the last read constant pool entry.
|
|
intptr_t ReadConstantPool(const Function& function,
|
|
const ObjectPool& pool,
|
|
intptr_t start_index);
|
|
|
|
BytecodePtr ReadBytecode(const ObjectPool& pool);
|
|
void ReadExceptionsTable(const Function& function,
|
|
const Bytecode& bytecode,
|
|
bool has_exceptions_table);
|
|
void ReadSourcePositions(const Bytecode& bytecode, bool has_source_positions);
|
|
void ReadLocalVariables(const Bytecode& bytecode, bool has_local_variables);
|
|
void ReadRecordedCoverage(const Bytecode& bytecode,
|
|
bool has_recorded_coverage);
|
|
StringPtr ConstructorName(const Class& cls, const String& name);
|
|
|
|
ObjectPtr ReadObjectContents(uint32_t header);
|
|
ObjectPtr ReadConstObject(intptr_t tag);
|
|
ObjectPtr ReadType(intptr_t tag, Nullability nullability);
|
|
StringPtr ReadString(bool is_canonical = true);
|
|
TypedDataPtr ReadLineStartsData(intptr_t line_starts_offset);
|
|
ScriptPtr ReadSourceFile(const String& uri, intptr_t offset);
|
|
TypeArgumentsPtr ReadTypeArguments();
|
|
void ReadAnnotations(const Class& cls,
|
|
const Object& declaration,
|
|
bool has_pragma);
|
|
void SetupFieldAccessorFunction(const Class& klass,
|
|
const Function& function,
|
|
const AbstractType& field_type);
|
|
PatchClassPtr GetPatchClass(const Class& cls, const Script& script);
|
|
InstancePtr Canonicalize(const Instance& instance);
|
|
|
|
// Similar to cls.EnsureClassDeclaration, but may be more efficient if
|
|
// class is from the current kernel binary.
|
|
void LoadReferencedClass(const Class& cls);
|
|
|
|
Reader reader_;
|
|
Thread* const thread_;
|
|
Zone* const zone_;
|
|
BytecodeComponentData* bytecode_component_;
|
|
Array* closures_ = nullptr;
|
|
PatchClass* patch_class_ = nullptr;
|
|
Array* functions_ = nullptr;
|
|
intptr_t function_index_ = 0;
|
|
GrowableArray<const FunctionType*> enclosing_function_types_;
|
|
Function& scoped_function_;
|
|
String& scoped_function_name_;
|
|
Class& scoped_function_class_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(BytecodeReaderHelper);
|
|
};
|
|
|
|
class BytecodeComponentData : ValueObject {
|
|
public:
|
|
enum {
|
|
kTypedData,
|
|
kVersion,
|
|
kStringsHeaderOffset,
|
|
kStringsContentsOffset,
|
|
kObjectOffsetsOffset,
|
|
kNumObjects,
|
|
kObjectsContentsOffset,
|
|
kMainOffset,
|
|
kNumLibraries,
|
|
kLibraryIndexOffset,
|
|
kLibrariesOffset,
|
|
kNumClasses,
|
|
kClassesOffset,
|
|
kMembersOffset,
|
|
kNumCodes,
|
|
kCodesOffset,
|
|
kSourcePositionsOffset,
|
|
kSourceFilesOffset,
|
|
kLineStartsOffset,
|
|
kLocalVariablesOffset,
|
|
kRecordedCoverageOffset,
|
|
kAnnotationsOffset,
|
|
kNumFields
|
|
};
|
|
|
|
explicit BytecodeComponentData(const Array& data) : data_(data) {}
|
|
|
|
TypedDataBasePtr GetTypedData() const;
|
|
intptr_t GetVersion() const;
|
|
intptr_t GetStringsHeaderOffset() const;
|
|
intptr_t GetStringsContentsOffset() const;
|
|
intptr_t GetObjectOffsetsOffset() const;
|
|
intptr_t GetNumObjects() const;
|
|
intptr_t GetObjectsContentsOffset() const;
|
|
intptr_t GetMainOffset() const;
|
|
intptr_t GetNumLibraries() const;
|
|
intptr_t GetLibraryIndexOffset() const;
|
|
intptr_t GetLibrariesOffset() const;
|
|
intptr_t GetNumClasses() const;
|
|
intptr_t GetClassesOffset() const;
|
|
intptr_t GetMembersOffset() const;
|
|
intptr_t GetNumCodes() const;
|
|
intptr_t GetCodesOffset() const;
|
|
intptr_t GetSourcePositionsOffset() const;
|
|
intptr_t GetSourceFilesOffset() const;
|
|
intptr_t GetLineStartsOffset() const;
|
|
intptr_t GetLocalVariablesOffset() const;
|
|
intptr_t GetRecordedCoverageOffset() const;
|
|
intptr_t GetAnnotationsOffset() const;
|
|
void SetObject(intptr_t index, const Object& obj) const;
|
|
ObjectPtr GetObject(intptr_t index) const;
|
|
|
|
bool IsNull() const { return data_.IsNull(); }
|
|
|
|
static ArrayPtr New(Zone* zone,
|
|
const TypedDataBase& typed_data,
|
|
intptr_t version,
|
|
intptr_t num_objects,
|
|
intptr_t strings_header_offset,
|
|
intptr_t strings_contents_offset,
|
|
intptr_t object_offsets_offset,
|
|
intptr_t objects_contents_offset,
|
|
intptr_t main_offset,
|
|
intptr_t num_libraries,
|
|
intptr_t library_index_offset,
|
|
intptr_t libraries_offset,
|
|
intptr_t num_classes,
|
|
intptr_t classes_offset,
|
|
intptr_t members_offset,
|
|
intptr_t num_codes,
|
|
intptr_t codes_offset,
|
|
intptr_t source_positions_offset,
|
|
intptr_t source_files_offset,
|
|
intptr_t line_starts_offset,
|
|
intptr_t local_variables_offset,
|
|
intptr_t recorded_coverage_offset,
|
|
intptr_t annotations_offset,
|
|
Heap::Space space);
|
|
|
|
private:
|
|
const Array& data_;
|
|
};
|
|
|
|
class BytecodeReader : public AllStatic {
|
|
public:
|
|
// Read declaration of the given class.
|
|
static void LoadClassDeclaration(const Class& cls);
|
|
|
|
// Read members of the given class.
|
|
static void FinishClassLoading(const Class& cls);
|
|
|
|
static void ReadParameterCovariance(const Function& function,
|
|
BitVector* is_covariant,
|
|
BitVector* is_generic_covariant_impl);
|
|
|
|
// Fills [token_positions] array with all token positions for the given
|
|
// script. Resulting array may have duplicates.
|
|
static void CollectScriptTokenPositionsFromBytecode(
|
|
const Script& interesting_script,
|
|
GrowableArray<intptr_t>* token_positions);
|
|
|
|
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
// Compute local variable descriptors for [function] with [bytecode].
|
|
static LocalVarDescriptorsPtr ComputeLocalVarDescriptors(
|
|
Zone* zone,
|
|
const Function& function,
|
|
const Bytecode& bytecode);
|
|
#endif
|
|
};
|
|
|
|
class BytecodeSourcePositionsIterator : ValueObject {
|
|
// These constants should match corresponding constants in class
|
|
// SourcePositions (pkg/dart2bytecode/lib/source_positions.dart).
|
|
static const intptr_t kNoSourcePosition = -1;
|
|
static const intptr_t kSyntheticFlag = 1 << 0;
|
|
static const intptr_t kYieldPointFlag = 1 << 1;
|
|
static const intptr_t kNumFlags = 2;
|
|
static const intptr_t kFlagMask = (1 << kNumFlags) - 1;
|
|
|
|
public:
|
|
BytecodeSourcePositionsIterator(Zone* zone, const Bytecode& bytecode)
|
|
: reader_(TypedDataBase::Handle(zone, bytecode.binary())) {
|
|
ASSERT(bytecode.HasSourcePositions());
|
|
reader_.set_offset(bytecode.source_positions_binary_offset());
|
|
pairs_remaining_ = reader_.ReadUInt();
|
|
}
|
|
|
|
bool MoveNext() {
|
|
if (pairs_remaining_ == 0) {
|
|
return false;
|
|
}
|
|
ASSERT(pairs_remaining_ > 0);
|
|
--pairs_remaining_;
|
|
cur_bci_ += reader_.ReadUInt();
|
|
cur_encoded_ += reader_.ReadSLEB128();
|
|
if (cur_encoded_ >= 0 || cur_encoded_ == kNoSourcePosition) {
|
|
cur_position_ = cur_encoded_;
|
|
flags_ = 0;
|
|
} else {
|
|
const intptr_t value = -cur_encoded_ - 1;
|
|
cur_position_ = value >> kNumFlags;
|
|
flags_ = value & kFlagMask;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uword PcOffset() const { return cur_bci_; }
|
|
|
|
TokenPosition TokenPos() const {
|
|
return ((flags_ & kSyntheticFlag) != 0)
|
|
? TokenPosition::Synthetic(cur_position_)
|
|
: cur_position_ == kNoSourcePosition
|
|
? TokenPosition::kNoSource
|
|
: TokenPosition::Deserialize(cur_position_);
|
|
}
|
|
|
|
bool IsYieldPoint() const { return (flags_ & kYieldPointFlag) != 0; }
|
|
|
|
private:
|
|
Reader reader_;
|
|
intptr_t pairs_remaining_ = 0;
|
|
intptr_t cur_bci_ = 0;
|
|
intptr_t cur_encoded_ = 0;
|
|
intptr_t cur_position_ = 0;
|
|
intptr_t flags_ = 0;
|
|
};
|
|
|
|
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
class BytecodeLocalVariablesIterator : ValueObject {
|
|
public:
|
|
// These constants should match corresponding constants in
|
|
// pkg/dart2bytecode/lib/local_variable_table.dart.
|
|
enum {
|
|
kInvalid,
|
|
kScope,
|
|
kVariableDeclaration,
|
|
kContextVariable,
|
|
};
|
|
|
|
static const char* kKindNames[];
|
|
static const intptr_t kKindMask = 0xF;
|
|
static const intptr_t kIsCapturedFlag = 1 << 4;
|
|
|
|
BytecodeLocalVariablesIterator(Zone* zone, const Bytecode& bytecode)
|
|
: reader_(TypedDataBase::Handle(zone, bytecode.binary())),
|
|
object_pool_(ObjectPool::Handle(zone, bytecode.object_pool())) {
|
|
if (bytecode.HasLocalVariablesInfo()) {
|
|
reader_.set_offset(bytecode.local_variables_binary_offset());
|
|
entries_remaining_ = reader_.ReadUInt();
|
|
}
|
|
}
|
|
|
|
bool MoveNext() {
|
|
if (entries_remaining_ <= 0) {
|
|
// Finished looking at the last entry, now we're done.
|
|
entries_remaining_ = -1;
|
|
return false;
|
|
}
|
|
--entries_remaining_;
|
|
cur_kind_and_flags_ = reader_.ReadByte();
|
|
cur_start_pc_ += reader_.ReadSLEB128();
|
|
switch (Kind()) {
|
|
case kScope:
|
|
cur_end_pc_ = cur_start_pc_ + reader_.ReadUInt();
|
|
cur_index_ = reader_.ReadSLEB128();
|
|
cur_token_pos_ = reader_.ReadPosition();
|
|
cur_end_token_pos_ = reader_.ReadPosition();
|
|
break;
|
|
case kVariableDeclaration:
|
|
cur_index_ = reader_.ReadSLEB128();
|
|
cur_name_ = reader_.ReadUInt();
|
|
cur_type_ = reader_.ReadUInt();
|
|
cur_declaration_token_pos_ = reader_.ReadPosition();
|
|
cur_token_pos_ = reader_.ReadPosition();
|
|
break;
|
|
case kContextVariable:
|
|
cur_index_ = reader_.ReadSLEB128();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Returns true after iterator moved past the last entry and
|
|
// MoveNext() returned false.
|
|
bool IsDone() const { return entries_remaining_ < 0; }
|
|
|
|
intptr_t Kind() const { return cur_kind_and_flags_ & kKindMask; }
|
|
const char* KindName() const { return kKindNames[Kind()]; }
|
|
bool IsScope() const { return Kind() == kScope; }
|
|
bool IsVariableDeclaration() const { return Kind() == kVariableDeclaration; }
|
|
bool IsContextVariable() const { return Kind() == kContextVariable; }
|
|
|
|
intptr_t StartPC() const { return cur_start_pc_; }
|
|
intptr_t EndPC() const {
|
|
ASSERT(IsScope() || IsVariableDeclaration());
|
|
return cur_end_pc_;
|
|
}
|
|
intptr_t ContextLevel() const {
|
|
ASSERT(IsScope());
|
|
return cur_index_;
|
|
}
|
|
TokenPosition StartTokenPos() const {
|
|
ASSERT(IsScope() || IsVariableDeclaration());
|
|
return cur_token_pos_;
|
|
}
|
|
TokenPosition EndTokenPos() const {
|
|
ASSERT(IsScope() || IsVariableDeclaration());
|
|
return cur_end_token_pos_;
|
|
}
|
|
intptr_t Index() const {
|
|
ASSERT(IsVariableDeclaration() || IsContextVariable());
|
|
return cur_index_;
|
|
}
|
|
StringPtr Name() const {
|
|
ASSERT(IsVariableDeclaration());
|
|
return String::RawCast(object_pool_.ObjectAt(cur_name_));
|
|
}
|
|
AbstractTypePtr Type() const {
|
|
ASSERT(IsVariableDeclaration());
|
|
return AbstractType::RawCast(object_pool_.ObjectAt(cur_type_));
|
|
}
|
|
TokenPosition DeclarationTokenPos() const {
|
|
ASSERT(IsVariableDeclaration());
|
|
return cur_declaration_token_pos_;
|
|
}
|
|
bool IsCaptured() const {
|
|
ASSERT(IsVariableDeclaration());
|
|
return (cur_kind_and_flags_ & kIsCapturedFlag) != 0;
|
|
}
|
|
|
|
private:
|
|
Reader reader_;
|
|
const ObjectPool& object_pool_;
|
|
intptr_t entries_remaining_ = 0;
|
|
intptr_t cur_kind_and_flags_ = 0;
|
|
intptr_t cur_start_pc_ = 0;
|
|
intptr_t cur_end_pc_ = 0;
|
|
intptr_t cur_index_ = -1;
|
|
intptr_t cur_name_ = -1;
|
|
intptr_t cur_type_ = -1;
|
|
TokenPosition cur_token_pos_ = TokenPosition::kNoSource;
|
|
TokenPosition cur_declaration_token_pos_ = TokenPosition::kNoSource;
|
|
TokenPosition cur_end_token_pos_ = TokenPosition::kNoSource;
|
|
};
|
|
|
|
// Types of recorded coverage, keep in sync with the RecordedCoverageType
|
|
// enum in pkg/dart2bytecode/lib/source_positions.dart.
|
|
enum class RecordedCoverageType { kRegular, kBranchTarget };
|
|
|
|
class BytecodeRecordedCoverageIterator : ValueObject {
|
|
public:
|
|
BytecodeRecordedCoverageIterator(Zone* zone, const Bytecode& bytecode)
|
|
: reader_(TypedDataBase::Handle(zone, bytecode.binary())) {
|
|
ASSERT(bytecode.HasRecordedCoverage());
|
|
reader_.set_offset(bytecode.recorded_coverage_binary_offset());
|
|
pairs_remaining_ = num_entries_ = reader_.ReadUInt();
|
|
}
|
|
|
|
intptr_t NumEntries() const { return num_entries_; }
|
|
|
|
bool MoveNext() {
|
|
if (pairs_remaining_ == 0) {
|
|
return false;
|
|
}
|
|
ASSERT(pairs_remaining_ > 0);
|
|
--pairs_remaining_;
|
|
cur_type_ = static_cast<RecordedCoverageType>(reader_.ReadUInt());
|
|
cur_position_ += reader_.ReadSLEB128();
|
|
return true;
|
|
}
|
|
|
|
intptr_t EncodedCoveragePosition() const {
|
|
return TokenPos().EncodeCoveragePosition(IsBranch());
|
|
}
|
|
|
|
private:
|
|
TokenPosition TokenPos() const {
|
|
ASSERT(cur_position_ >= 0);
|
|
return TokenPosition::Deserialize(cur_position_);
|
|
}
|
|
|
|
bool IsBranch() const {
|
|
return cur_type_ == RecordedCoverageType::kBranchTarget;
|
|
}
|
|
|
|
Reader reader_;
|
|
intptr_t num_entries_ = 0;
|
|
intptr_t pairs_remaining_ = 0;
|
|
RecordedCoverageType cur_type_ = RecordedCoverageType::kRegular;
|
|
intptr_t cur_position_ = 0;
|
|
};
|
|
|
|
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
} // namespace bytecode
|
|
} // namespace dart
|
|
|
|
#endif // defined(DART_DYNAMIC_MODULES)
|
|
#endif // RUNTIME_VM_BYTECODE_READER_H_
|