blob: 920c1d2c0ab17c396951b449243ead29d4621d5d [file] [log] [blame]
//===--- ReflectionContext.h - Swift Type Reflection Context ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Implements the context for reflection of values in the address space of a
// remote process.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_REFLECTION_REFLECTIONCONTEXT_H
#define SWIFT_REFLECTION_REFLECTIONCONTEXT_H
#include "swift/Remote/MemoryReader.h"
#include "swift/Remote/MetadataReader.h"
#include "swift/Reflection/Records.h"
#include "swift/Reflection/TypeLowering.h"
#include "swift/Reflection/TypeRef.h"
#include "swift/Reflection/TypeRefBuilder.h"
#include "swift/Runtime/Unreachable.h"
#include <iostream>
#include <set>
#include <vector>
#include <unordered_map>
namespace swift {
namespace reflection {
using swift::remote::MemoryReader;
using swift::remote::RemoteAddress;
template <typename Runtime>
class ReflectionContext
: public remote::MetadataReader<Runtime, TypeRefBuilder> {
using super = remote::MetadataReader<Runtime, TypeRefBuilder>;
using super::readMetadata;
using super::readObjCClassName;
std::unordered_map<typename super::StoredPointer, const TypeInfo *> Cache;
public:
using super::getBuilder;
using super::readIsaMask;
using super::readTypeFromMetadata;
using super::readGenericArgFromMetadata;
using super::readMetadataFromInstance;
using typename super::StoredPointer;
explicit ReflectionContext(std::shared_ptr<MemoryReader> reader)
: super(std::move(reader)) {}
ReflectionContext(const ReflectionContext &other) = delete;
ReflectionContext &operator=(const ReflectionContext &other) = delete;
MemoryReader &getReader() {
return *this->Reader;
}
unsigned getSizeOfHeapObject() {
// This must match sizeof(HeapObject) for the target.
return sizeof(StoredPointer) + 8;
}
void dumpAllSections(std::ostream &OS) {
getBuilder().dumpAllSections();
}
void addReflectionInfo(ReflectionInfo I) {
getBuilder().addReflectionInfo(I);
}
/// Return a description of the layout of a class instance with the given
/// metadata as its isa pointer.
const TypeInfo *getMetadataTypeInfo(StoredPointer MetadataAddress) {
// See if we cached the layout already
auto found = Cache.find(MetadataAddress);
if (found != Cache.end())
return found->second;
auto &TC = getBuilder().getTypeConverter();
const TypeInfo *TI = nullptr;
auto TR = readTypeFromMetadata(MetadataAddress);
auto kind = this->readKindFromMetadata(MetadataAddress);
if (TR != nullptr && kind.first) {
switch (kind.second) {
case MetadataKind::Class: {
// Figure out where the stored properties of this class begin
// by looking at the size of the superclass
bool valid;
unsigned start;
std::tie(valid, start) =
this->readInstanceStartAndAlignmentFromClassMetadata(MetadataAddress);
// Perform layout
if (valid)
TI = TC.getClassInstanceTypeInfo(TR, start);
break;
}
default:
break;
}
}
// Cache the result for future lookups
Cache[MetadataAddress] = TI;
return TI;
}
/// Return a description of the layout of a class instance with the given
/// metadata as its isa pointer.
const TypeInfo *getInstanceTypeInfo(StoredPointer ObjectAddress) {
auto MetadataAddress = readMetadataFromInstance(ObjectAddress);
if (!MetadataAddress.first)
return nullptr;
auto kind = this->readKindFromMetadata(MetadataAddress.second);
if (!kind.first)
return nullptr;
switch (kind.second) {
case MetadataKind::Class:
return getMetadataTypeInfo(MetadataAddress.second);
case MetadataKind::HeapLocalVariable: {
auto CDAddr = this->readCaptureDescriptorFromMetadata(MetadataAddress.second);
if (!CDAddr.first)
return nullptr;
// FIXME: Non-generic SIL boxes also use the HeapLocalVariable metadata
// kind, but with a null capture descriptor right now (see
// FixedBoxTypeInfoBase::allocate).
//
// Non-generic SIL boxes share metadata among types with compatible
// layout, but we need some way to get an outgoing pointer map for them.
auto *CD = getBuilder().getCaptureDescriptor(CDAddr.second);
if (CD == nullptr)
return nullptr;
auto Info = getBuilder().getClosureContextInfo(*CD);
return getClosureContextInfo(ObjectAddress, Info);
}
case MetadataKind::HeapGenericLocalVariable: {
// Generic SIL @box type - there is always an instantiated metadata
// pointer for the boxed type.
if (auto Meta = readMetadata(MetadataAddress.second)) {
auto GenericHeapMeta =
cast<TargetGenericBoxHeapMetadata<Runtime>>(Meta.getLocalBuffer());
return getMetadataTypeInfo(GenericHeapMeta->BoxedType);
}
return nullptr;
}
case MetadataKind::ErrorObject:
// Error boxed existential on non-Objective-C runtime target
return nullptr;
default:
return nullptr;
}
}
bool
projectExistential(RemoteAddress ExistentialAddress,
const TypeRef *ExistentialTR,
const TypeRef **OutInstanceTR,
RemoteAddress *OutInstanceAddress) {
if (ExistentialTR == nullptr)
return false;
auto ExistentialTI = getTypeInfo(ExistentialTR);
if (ExistentialTI == nullptr)
return false;
auto ExistentialRecordTI = dyn_cast<const RecordTypeInfo>(ExistentialTI);
if (ExistentialRecordTI == nullptr)
return false;
switch (ExistentialRecordTI->getRecordKind()) {
// Class existentials have trivial layout.
// It is itself the pointer to the instance followed by the witness tables.
case RecordKind::ClassExistential:
// This is just Builtin.UnknownObject
*OutInstanceTR = ExistentialRecordTI->getFields()[0].TR;
*OutInstanceAddress = ExistentialAddress;
return true;
// Opaque existentials fall under two cases:
// If the value fits in three words, it starts at the beginning of the
// container. If it doesn't, the first word is a pointer to a heap box.
case RecordKind::OpaqueExistential: {
auto Fields = ExistentialRecordTI->getFields();
auto ExistentialMetadataField = std::find_if(Fields.begin(), Fields.end(),
[](const FieldInfo &FI) -> bool {
return FI.Name.compare("metadata") == 0;
});
if (ExistentialMetadataField == Fields.end())
return false;
// Get the metadata pointer for the contained instance type.
// This is equivalent to:
// auto PointerArray = reinterpret_cast<uintptr_t*>(ExistentialAddress);
// uintptr_t MetadataAddress = PointerArray[Offset];
auto MetadataAddressAddress
= RemoteAddress(ExistentialAddress.getAddressData() +
ExistentialMetadataField->Offset);
StoredPointer MetadataAddress = 0;
if (!getReader().readInteger(MetadataAddressAddress, &MetadataAddress))
return false;
auto InstanceTR = readTypeFromMetadata(MetadataAddress);
if (!InstanceTR)
return false;
*OutInstanceTR = InstanceTR;
auto InstanceTI = getTypeInfo(InstanceTR);
if (!InstanceTI)
return false;
if (InstanceTI->getSize() <= ExistentialMetadataField->Offset) {
// The value fits in the existential container, so it starts at the
// start of the container.
*OutInstanceAddress = ExistentialAddress;
} else {
// Otherwise it's in a box somewhere off in the heap. The first word
// of the container has the address to that box.
StoredPointer BoxAddress = 0;
if (!getReader().readInteger(ExistentialAddress, &BoxAddress))
return false;
// Address = BoxAddress + (sizeof(HeapObject) + alignMask) & ~alignMask)
auto Alignment = InstanceTI->getAlignment();
auto StartOfValue = BoxAddress + getSizeOfHeapObject();
// Align.
StartOfValue += Alignment - StartOfValue % Alignment;
*OutInstanceAddress = RemoteAddress(StartOfValue);
}
return true;
}
case RecordKind::ErrorExistential: {
// We have a pointer to an error existential, which is always heap object.
bool successfullyGotIsa = false;
StoredPointer MetadataAddress = 0;
std::tie(successfullyGotIsa, MetadataAddress)
= readMetadataFromInstance(ExistentialAddress.getAddressData());
if (!successfullyGotIsa)
return false;
bool isObjC = false;
// If we can determine the Objective-C class name, this is probably an
// error existential with NSError-compatible layout.
std::string ObjCClassName;
if (readObjCClassName(MetadataAddress, ObjCClassName)) {
if (ObjCClassName == "_SwiftNativeNSError")
isObjC = true;
} else {
// Otherwise, we can check to see if this is a class metadata with the
// kind value's least significant bit set, which indicates a pure
// Swift class.
auto Meta = readMetadata(MetadataAddress);
auto ClassMeta = dyn_cast<TargetClassMetadata<Runtime>>(Meta);
if (!ClassMeta)
return false;
isObjC = ClassMeta->isPureObjC();
}
// In addition to the isa pointer and two 32-bit reference counts, if the
// error existential is layout-compatible with NSError, we also need to
// skip over its three word-sized fields: the error code, the domain,
// and userInfo.
StoredPointer InstanceMetadataAddressAddress
= ExistentialAddress.getAddressData() +
(isObjC ? 5 : 2) * sizeof(StoredPointer);
// We need to get the instance's alignment info so we can get the exact
// offset of the start of its data in the class.
StoredPointer InstanceMetadataAddress = 0;
std::tie(successfullyGotIsa, InstanceMetadataAddress) =
readMetadataFromInstance(InstanceMetadataAddressAddress);
if (!successfullyGotIsa)
return false;
auto InstanceTR = readTypeFromMetadata(InstanceMetadataAddress);
if (!InstanceTR)
return false;
auto InstanceTI = getTypeInfo(InstanceTR);
if (!InstanceTI)
return false;
// Now we need to skip over the instance metadata pointer and instance's
// conformance pointer for Swift.Error.
StoredPointer InstanceAddress = InstanceMetadataAddressAddress +
2 * sizeof(StoredPointer);
// Round up to alignment, and we have the start address of the
// instance payload.
auto Alignment = InstanceTI->getAlignment();
InstanceAddress += Alignment - InstanceAddress % Alignment;
*OutInstanceTR = InstanceTR;
*OutInstanceAddress = RemoteAddress(InstanceAddress);
return true;
}
default:
return false;
}
}
/// Return a description of the layout of a value with the given type.
const TypeInfo *getTypeInfo(const TypeRef *TR) {
return getBuilder().getTypeConverter().getTypeInfo(TR);
}
private:
const TypeInfo *getClosureContextInfo(StoredPointer Context,
const ClosureContextInfo &Info) {
RecordTypeInfoBuilder Builder(getBuilder().getTypeConverter(),
RecordKind::ClosureContext);
auto Metadata = readMetadataFromInstance(Context);
if (!Metadata.first)
return nullptr;
// Calculate the offset of the first capture.
// See GenHeap.cpp, buildPrivateMetadata().
auto OffsetToFirstCapture =
this->readOffsetToFirstCaptureFromMetadata(Metadata.second);
if (!OffsetToFirstCapture.first)
return nullptr;
// Initialize the builder.
Builder.addField(OffsetToFirstCapture.second, sizeof(StoredPointer),
/*numExtraInhabitants=*/0);
// Skip the closure's necessary bindings struct, if it's present.
auto SizeOfNecessaryBindings = Info.NumBindings * sizeof(StoredPointer);
Builder.addField(SizeOfNecessaryBindings, sizeof(StoredPointer),
/*numExtraInhabitants=*/0);
// FIXME: should be unordered_set but I'm too lazy to write a hash
// functor
std::set<std::pair<const TypeRef *, const MetadataSource *>> Done;
GenericArgumentMap Subs;
ArrayRef<const TypeRef *> CaptureTypes = Info.CaptureTypes;
// Closure context element layout depends on the layout of the
// captured types, but captured types might depend on element
// layout (of previous elements). Use an iterative approach to
// solve the problem.
while (!CaptureTypes.empty()) {
const TypeRef *OrigCaptureTR = CaptureTypes[0];
// If we failed to demangle the capture type, we cannot proceed.
if (OrigCaptureTR == nullptr)
return nullptr;
const TypeRef *SubstCaptureTR = nullptr;
// If we have enough substitutions to make this captured value's
// type concrete, or we know it's size anyway (because it is a
// class reference or metatype, for example), go ahead and add
// it to the layout.
if (OrigCaptureTR->isConcreteAfterSubstitutions(Subs))
SubstCaptureTR = OrigCaptureTR->subst(getBuilder(), Subs);
else if (getBuilder().getTypeConverter().hasFixedSize(OrigCaptureTR))
SubstCaptureTR = OrigCaptureTR;
if (SubstCaptureTR != nullptr) {
Builder.addField("", SubstCaptureTR);
if (Builder.isInvalid())
return nullptr;
// Try the next capture type.
CaptureTypes = CaptureTypes.slice(1);
continue;
}
// Ok, we do not have enough substitutions yet. Perhaps we have
// enough elements figured out that we can pick off some
// metadata sources though, and use those to derive some new
// substitutions.
bool Progress = false;
for (auto Source : Info.MetadataSources) {
// Don't read a source more than once.
if (Done.count(Source))
continue;
// If we don't have enough information to read this source
// (because it is fulfilled by metadata from a capture at
// at unknown offset), keep going.
if (!isMetadataSourceReady(Source.second, Builder))
continue;
auto Metadata = readMetadataSource(Context, Source.second, Builder);
if (!Metadata.first)
return nullptr;
auto *SubstTR = readTypeFromMetadata(Metadata.second);
if (SubstTR == nullptr)
return nullptr;
if (!TypeRef::deriveSubstitutions(Subs, Source.first, SubstTR))
return nullptr;
Done.insert(Source);
Progress = true;
}
// If we failed to make any forward progress above, we're stuck
// and cannot close out this layout.
if (!Progress)
return nullptr;
}
// Ok, we have a complete picture now.
return Builder.build();
}
/// Checks if we have enough information to read the given metadata
/// source.
///
/// \param Builder Used to obtain offsets of elements known so far.
bool isMetadataSourceReady(const MetadataSource *MS,
const RecordTypeInfoBuilder &Builder) {
switch (MS->getKind()) {
case MetadataSourceKind::ClosureBinding:
return true;
case MetadataSourceKind::ReferenceCapture: {
unsigned Index = cast<ReferenceCaptureMetadataSource>(MS)->getIndex();
return Index < Builder.getNumFields();
}
case MetadataSourceKind::MetadataCapture: {
unsigned Index = cast<MetadataCaptureMetadataSource>(MS)->getIndex();
return Index < Builder.getNumFields();
}
case MetadataSourceKind::GenericArgument: {
auto Base = cast<GenericArgumentMetadataSource>(MS)->getSource();
return isMetadataSourceReady(Base, Builder);
}
case MetadataSourceKind::Self:
case MetadataSourceKind::SelfWitnessTable:
return true;
}
swift_runtime_unreachable("Unhandled MetadataSourceKind in switch.");
}
/// Read metadata for a captured generic type from a closure context.
///
/// \param Context The closure context in the remote process.
///
/// \param MS The metadata source, which must be "ready" as per the
/// above.
///
/// \param Builder Used to obtain offsets of elements known so far.
std::pair<bool, StoredPointer>
readMetadataSource(StoredPointer Context,
const MetadataSource *MS,
const RecordTypeInfoBuilder &Builder) {
switch (MS->getKind()) {
case MetadataSourceKind::ClosureBinding: {
unsigned Index = cast<ClosureBindingMetadataSource>(MS)->getIndex();
// Skip the context's isa pointer (4 or 8 bytes) and reference counts
// (4 bytes each regardless of platform word size). This is just
// sizeof(HeapObject) in the target.
//
// Metadata and conformance tables are stored consecutively after
// the heap object header, in the 'necessary bindings' area.
//
// We should only have the index of a type metadata record here.
unsigned Offset = getSizeOfHeapObject() +
sizeof(StoredPointer) * Index;
StoredPointer MetadataAddress;
if (!getReader().readInteger(RemoteAddress(Context + Offset),
&MetadataAddress))
break;
return std::make_pair(true, MetadataAddress);
}
case MetadataSourceKind::ReferenceCapture: {
unsigned Index = cast<ReferenceCaptureMetadataSource>(MS)->getIndex();
// We should already have enough type information to know the offset
// of this capture in the context.
unsigned CaptureOffset = Builder.getFieldOffset(Index);
StoredPointer CaptureAddress;
if (!getReader().readInteger(RemoteAddress(Context + CaptureOffset),
&CaptureAddress))
break;
// Read the requested capture's isa pointer.
return readMetadataFromInstance(CaptureAddress);
}
case MetadataSourceKind::MetadataCapture: {
unsigned Index = cast<MetadataCaptureMetadataSource>(MS)->getIndex();
// We should already have enough type information to know the offset
// of this capture in the context.
unsigned CaptureOffset = Builder.getFieldOffset(Index);
StoredPointer CaptureAddress;
if (!getReader().readInteger(RemoteAddress(Context + CaptureOffset),
&CaptureAddress))
break;
return std::make_pair(true, CaptureAddress);
}
case MetadataSourceKind::GenericArgument: {
auto *GAMS = cast<GenericArgumentMetadataSource>(MS);
auto Base = readMetadataSource(Context, GAMS->getSource(), Builder);
if (!Base.first)
break;
unsigned Index = GAMS->getIndex();
auto Arg = readGenericArgFromMetadata(Base.second, Index);
if (!Arg.first)
break;
return Arg;
}
case MetadataSourceKind::Self:
case MetadataSourceKind::SelfWitnessTable:
break;
}
return std::make_pair(false, StoredPointer(0));
}
};
} // end namespace reflection
} // end namespace swift
#endif // SWIFT_REFLECTION_REFLECTIONCONTEXT_H