//===--- RemoteAST.cpp ----------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // // This file implements the RemoteAST interface. // //===----------------------------------------------------------------------===// #include "swift/RemoteAST/RemoteAST.h" #include "swift/Remote/MetadataReader.h" #include "swift/Strings.h" #include "swift/Subsystems.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/Types.h" #include "swift/AST/TypeRepr.h" #include "swift/Basic/Mangler.h" #include "swift/ClangImporter/ClangImporter.h" #include "llvm/ADT/StringSwitch.h" // TODO: Develop a proper interface for this. #include "swift/AST/IRGenOptions.h" #include "swift/AST/SILOptions.h" #include "swift/SIL/SILModule.h" #include "../IRGen/IRGenModule.h" #include "../IRGen/FixedTypeInfo.h" #include "../IRGen/GenClass.h" #include "../IRGen/GenStruct.h" #include "../IRGen/GenTuple.h" #include "../IRGen/MemberAccessStrategy.h" using namespace swift; using namespace swift::remote; using namespace swift::remoteAST; using irgen::Alignment; using irgen::Size; static inline RemoteAddress operator+(RemoteAddress address, Size offset) { return RemoteAddress(address.getAddressData() + offset.getValue()); } namespace { /// A "minimal" class for querying IRGen. struct IRGenContext { IRGenOptions IROpts; SILOptions SILOpts; std::unique_ptr SILMod; llvm::LLVMContext LLVMContext; irgen::IRGenerator IRGen; irgen::IRGenModule IGM; private: IRGenContext(ASTContext &ctx, ModuleDecl *module) : IROpts(createIRGenOptions()), SILMod(SILModule::createEmptyModule(module, SILOpts)), IRGen(IROpts, *SILMod), IGM(IRGen, IRGen.createTargetMachine(), /*SourceFile*/ nullptr, LLVMContext, "", "", "") {} static IRGenOptions createIRGenOptions() { IRGenOptions IROpts; IROpts.EnableResilienceBypass = true; return IROpts; } public: static std::unique_ptr create(ASTContext &ctx, DeclContext *nominalDC) { auto module = nominalDC->getParentModule(); return std::unique_ptr(new IRGenContext(ctx, module)); } }; /// An implementation of MetadataReader's BuilderType concept that /// just finds and builds things in the AST. class RemoteASTTypeBuilder { ASTContext &Ctx; Demangle::NodeFactory Factory; /// The notional context in which we're writing and type-checking code. /// Created lazily. DeclContext *NotionalDC = nullptr; Optional CurFailure; public: using BuiltType = swift::Type; using BuiltNominalTypeDecl = swift::NominalTypeDecl *; using BuiltProtocolDecl = swift::ProtocolDecl *; explicit RemoteASTTypeBuilder(ASTContext &ctx) : Ctx(ctx) {} std::unique_ptr createIRGenContext() { return IRGenContext::create(Ctx, getNotionalDC()); } template Result fail(FailureKindTy kind, FailureArgTys &&...failureArgs) { if (!CurFailure) { CurFailure.emplace(kind, std::forward(failureArgs)...); } return Result(); } template Result getFailureAsResult(DefaultFailureKindTy defaultFailureKind, DefaultFailureArgTys &&...defaultFailureArgs) { // If we already have a failure, use that. if (CurFailure) { Result result = std::move(*CurFailure); CurFailure.reset(); return result; } // Otherwise, use the default failure. return Result::emplaceFailure(defaultFailureKind, std::forward(defaultFailureArgs)...); } Demangle::NodeFactory &getNodeFactory() { return Factory; } Type createBuiltinType(const std::string &mangledName) { // TODO return Type(); } NominalTypeDecl *createNominalTypeDecl(StringRef mangledName) { Demangle::Demangler Dem; Demangle::NodePointer node = Dem.demangleType(mangledName); if (!node) return nullptr; return createNominalTypeDecl(node); } NominalTypeDecl *createNominalTypeDecl(const Demangle::NodePointer &node); ProtocolDecl *createProtocolDecl(const Demangle::NodePointer &node) { return dyn_cast_or_null(createNominalTypeDecl(node)); } Type createNominalType(NominalTypeDecl *decl) { // If the declaration is generic, fail. if (decl->isGenericContext()) return Type(); return decl->getDeclaredType(); } Type createNominalType(NominalTypeDecl *decl, Type parent) { // If the declaration is generic, fail. if (decl->getGenericParams()) return Type(); // Validate the parent type. if (!validateNominalParent(decl, parent)) return Type(); return NominalType::get(decl, parent, Ctx); } Type createBoundGenericType(NominalTypeDecl *decl, ArrayRef args) { // If the declaration isn't generic, fail. if (!decl->isGenericContext()) return Type(); // Build a SubstitutionMap. auto *genericSig = decl->getGenericSignature(); auto genericParams = genericSig->getSubstitutableParams(); if (genericParams.size() != args.size()) return Type(); auto subMap = SubstitutionMap::get( genericSig, [&](SubstitutableType *t) -> Type { for (unsigned i = 0, e = genericParams.size(); i < e; ++i) { if (t->isEqual(genericParams[i])) return args[i]; } return Type(); }, // FIXME: Wrong module LookUpConformanceInModule(decl->getParentModule())); auto origType = decl->getDeclaredInterfaceType(); // FIXME: We're not checking that the type satisfies the generic // requirements of the signature here. auto substType = origType.subst(subMap); return substType; } Type createBoundGenericType(NominalTypeDecl *decl, ArrayRef args, Type parent) { // If the declaration isn't generic, fail. if (!decl->getGenericParams()) return Type(); // Validate the parent type. if (!validateNominalParent(decl, parent)) return Type(); // Make a generic type repr that's been resolved to this decl. TypeReprList genericArgReprs(args); auto genericRepr = GenericIdentTypeRepr::create(Ctx, SourceLoc(), decl->getName(), genericArgReprs.getList(), SourceRange()); // FIXME genericRepr->setValue(decl, nullptr); Type genericType; // If we have a parent type, we need to build a compound type repr. if (parent) { // Life would be much easier if we could just use a FixedTypeRepr for // the parent. But we can't! So we have to recursively expand // like this; and recursing with a lambda isn't impossible, so it gets // even worse. SmallVector ancestry; for (auto p = parent; p; p = p->getNominalParent()) { ancestry.push_back(p); } struct GenericRepr { TypeReprList GenericArgs; GenericIdentTypeRepr *Ident; GenericRepr(const ASTContext &Ctx, BoundGenericType *type) : GenericArgs(type->getGenericArgs()), Ident(GenericIdentTypeRepr::create(Ctx, SourceLoc(), type->getDecl()->getName(), GenericArgs.getList(), SourceRange())) { // FIXME Ident->setValue(type->getDecl(), nullptr); } // SmallVector::emplace_back will never need to call this because // we reserve the right size, but it does try statically. GenericRepr(const GenericRepr &other) : GenericArgs({}), Ident(nullptr) { llvm_unreachable("should not be called dynamically"); } }; // Pre-allocate the component vectors so that we can form references // into them safely. SmallVector simpleComponents; SmallVector genericComponents; simpleComponents.reserve(ancestry.size()); genericComponents.reserve(ancestry.size()); // Build the parent hierarchy. SmallVector componentReprs; for (size_t i = ancestry.size(); i != 0; --i) { Type p = ancestry[i - 1]; if (auto boundGeneric = p->getAs()) { genericComponents.emplace_back(Ctx, boundGeneric); componentReprs.push_back(genericComponents.back().Ident); } else { auto nominal = p->castTo(); simpleComponents.emplace_back(SourceLoc(), nominal->getDecl()->getName()); // FIXME simpleComponents.back().setValue(nominal->getDecl(), nullptr); componentReprs.push_back(&simpleComponents.back()); } } componentReprs.push_back(genericRepr); auto compoundRepr = CompoundIdentTypeRepr::create(Ctx, componentReprs); genericType = checkTypeRepr(compoundRepr); } else { genericType = checkTypeRepr(genericRepr); } // If type-checking failed, we've failed. if (!genericType) return Type(); // Validate that we used the right decl. if (auto bgt = genericType->getAs()) { if (bgt->getDecl() != decl) return Type(); } return genericType; } Type createTupleType(ArrayRef eltTypes, StringRef labels, bool isVariadic) { // Just bail out on variadic tuples for now. if (isVariadic) return Type(); SmallVector elements; elements.reserve(eltTypes.size()); for (auto eltType : eltTypes) { Identifier label; if (!labels.empty()) { auto split = labels.split(' '); if (!split.first.empty()) label = Ctx.getIdentifier(split.first); labels = split.second; } elements.emplace_back(eltType, label); } return TupleType::get(elements, Ctx); } Type createFunctionType(ArrayRef> params, Type output, FunctionTypeFlags flags) { FunctionTypeRepresentation representation; switch (flags.getConvention()) { case FunctionMetadataConvention::Swift: representation = FunctionTypeRepresentation::Swift; break; case FunctionMetadataConvention::Block: representation = FunctionTypeRepresentation::Block; break; case FunctionMetadataConvention::Thin: representation = FunctionTypeRepresentation::Thin; break; case FunctionMetadataConvention::CFunctionPointer: representation = FunctionTypeRepresentation::CFunctionPointer; break; } auto einfo = AnyFunctionType::ExtInfo(representation, /*throws*/ flags.throws()); if (flags.isEscaping()) einfo = einfo.withNoEscape(false); else einfo = einfo.withNoEscape(true); // The result type must be materializable. if (!output->isMaterializable()) return Type(); llvm::SmallVector funcParams; for (const auto ¶m : params) { auto type = param.getType(); // All the argument types must be materializable. if (!type->isMaterializable()) return Type(); auto label = Ctx.getIdentifier(param.getLabel()); auto flags = param.getFlags(); auto ownership = flags.getValueOwnership(); auto parameterFlags = ParameterTypeFlags() .withInOut(ownership == ValueOwnership::InOut) .withShared(ownership == ValueOwnership::Shared) .withOwned(ownership == ValueOwnership::Owned) .withVariadic(flags.isVariadic()); funcParams.push_back(AnyFunctionType::Param(type, label, parameterFlags)); } return FunctionType::get(funcParams, output, einfo); } Type createProtocolCompositionType(ArrayRef protocols, Type superclass, bool isClassBound) { std::vector members; for (auto protocol : protocols) members.push_back(protocol->getDeclaredType()); if (superclass && superclass->getClassOrBoundGenericClass()) members.push_back(superclass); return ProtocolCompositionType::get(Ctx, members, isClassBound); } Type createExistentialMetatypeType(Type instance) { if (!instance->isAnyExistentialType()) return Type(); return ExistentialMetatypeType::get(instance); } Type createMetatypeType(Type instance, bool wasAbstract=false) { // FIXME: Plumb through metatype representation and generalize silly // 'wasAbstract' flag return MetatypeType::get(instance); } Type createGenericTypeParameterType(unsigned depth, unsigned index) { return GenericTypeParamType::get(depth, index, Ctx); } Type createDependentMemberType(StringRef member, Type base, ProtocolDecl *protocol) { if (!base->isTypeParameter()) return Type(); for (auto member : protocol->lookupDirect(Ctx.getIdentifier(member), /*ignoreNew=*/true)) { if (auto assocType = dyn_cast(member)) return DependentMemberType::get(base, assocType); } return Type(); } Type createUnownedStorageType(Type base) { if (!base->allowsOwnership()) return Type(); return UnownedStorageType::get(base, Ctx); } Type createUnmanagedStorageType(Type base) { if (!base->allowsOwnership()) return Type(); return UnmanagedStorageType::get(base, Ctx); } Type createWeakStorageType(Type base) { if (!base->allowsOwnership()) return Type(); return WeakStorageType::get(base, Ctx); } Type createSILBoxType(Type base) { return SILBoxType::get(base->getCanonicalType()); } Type createObjCClassType(StringRef name) { auto typeDecl = findForeignNominalTypeDecl(name, /*relatedEntityKind*/{}, ForeignModuleKind::Imported, Demangle::Node::Kind::Class); if (!typeDecl) return Type(); return createNominalType(typeDecl, /*parent*/ Type()); } Type createForeignClassType(StringRef mangledName) { auto typeDecl = createNominalTypeDecl(mangledName); if (!typeDecl) return Type(); return createNominalType(typeDecl, /*parent*/ Type()); } Type getUnnamedForeignClassType() { return Type(); } Type getOpaqueType() { return Type(); } private: bool validateNominalParent(NominalTypeDecl *decl, Type parent) { auto parentDecl = decl->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext(); // If we don't have a parent type, fast-path. if (!parent) { return parentDecl == nullptr; } // We do have a parent type. If the nominal type doesn't, it's an error. if (!parentDecl) { return false; } // FIXME: validate that the parent is a correct application of the // enclosing context? return true; } DeclContext *findDeclContext(const Demangle::NodePointer &node); ModuleDecl *findModule(const Demangle::NodePointer &node); Demangle::NodePointer findModuleNode(const Demangle::NodePointer &node); enum class ForeignModuleKind { Imported, SynthesizedByImporter }; Optional getForeignModuleKind(const Demangle::NodePointer &node); NominalTypeDecl *findNominalTypeDecl(DeclContext *dc, Identifier name, Identifier privateDiscriminator, Demangle::Node::Kind kind); NominalTypeDecl *findForeignNominalTypeDecl(StringRef name, StringRef relatedEntityKind, ForeignModuleKind lookupKind, Demangle::Node::Kind kind); Type checkTypeRepr(TypeRepr *repr) { DeclContext *dc = getNotionalDC(); TypeLoc loc(repr); if (performTypeLocChecking(Ctx, loc, dc, /*diagnose*/ false)) return Type(); return loc.getType(); } static NominalTypeDecl *getAcceptableNominalTypeCandidate(ValueDecl *decl, Demangle::Node::Kind kind) { if (kind == Demangle::Node::Kind::Class) { return dyn_cast(decl); } else if (kind == Demangle::Node::Kind::Enum) { return dyn_cast(decl); } else if (kind == Demangle::Node::Kind::Protocol) { return dyn_cast(decl); } else { assert(kind == Demangle::Node::Kind::Structure); return dyn_cast(decl); } } DeclContext *getNotionalDC() { if (!NotionalDC) { NotionalDC = ModuleDecl::create(Ctx.getIdentifier(".RemoteAST"), Ctx); NotionalDC = new (Ctx) TopLevelCodeDecl(NotionalDC); } return NotionalDC; } class TypeReprList { SmallVector Reprs; SmallVector Refs; public: explicit TypeReprList(ArrayRef types) { Reprs.reserve(types.size()); Refs.reserve(types.size()); for (auto type : types) { Reprs.emplace_back(type, SourceLoc()); Refs.push_back(&Reprs.back()); } } ArrayRef getList() const { return Refs; } }; }; } // end anonymous namespace NominalTypeDecl * RemoteASTTypeBuilder::createNominalTypeDecl(const Demangle::NodePointer &node) { auto DC = findDeclContext(node); if (!DC) { return fail(Failure::CouldNotResolveTypeDecl, Demangle::mangleNode(node)); } auto decl = dyn_cast(DC); if (!decl) return nullptr; return decl; } ModuleDecl *RemoteASTTypeBuilder::findModule(const Demangle::NodePointer &node){ assert(node->getKind() == Demangle::Node::Kind::Module); const auto &moduleName = node->getText(); return Ctx.getModuleByName(moduleName); } Demangle::NodePointer RemoteASTTypeBuilder::findModuleNode(const Demangle::NodePointer &node) { if (node->getKind() == Demangle::Node::Kind::Module) return node; if (!node->hasChildren()) return nullptr; const auto &child = node->getFirstChild(); if (child->getKind() != Demangle::Node::Kind::DeclContext) return nullptr; return findModuleNode(child->getFirstChild()); } Optional RemoteASTTypeBuilder::getForeignModuleKind(const Demangle::NodePointer &node) { if (node->getKind() == Demangle::Node::Kind::DeclContext) return getForeignModuleKind(node->getFirstChild()); if (node->getKind() != Demangle::Node::Kind::Module) return None; return llvm::StringSwitch>(node->getText()) .Case(MANGLING_MODULE_OBJC, ForeignModuleKind::Imported) .Case(MANGLING_MODULE_CLANG_IMPORTER, ForeignModuleKind::SynthesizedByImporter) .Default(None); } DeclContext * RemoteASTTypeBuilder::findDeclContext(const Demangle::NodePointer &node) { switch (node->getKind()) { case Demangle::Node::Kind::DeclContext: case Demangle::Node::Kind::Type: return findDeclContext(node->getFirstChild()); case Demangle::Node::Kind::Module: return findModule(node); case Demangle::Node::Kind::Class: case Demangle::Node::Kind::Enum: case Demangle::Node::Kind::Protocol: case Demangle::Node::Kind::Structure: case Demangle::Node::Kind::TypeAlias: { const auto &declNameNode = node->getChild(1); // Handle local declarations. if (declNameNode->getKind() == Demangle::Node::Kind::LocalDeclName) { // Find the AST node for the defining module. auto moduleNode = findModuleNode(node); if (!moduleNode) return nullptr; auto module = findModule(moduleNode); if (!module) return nullptr; // Look up the local type by its mangling. auto mangledName = Demangle::mangleNode(node); auto decl = module->lookupLocalType(mangledName); if (!decl) return nullptr; return dyn_cast(decl); } StringRef name; StringRef relatedEntityKind; Identifier privateDiscriminator; if (declNameNode->getKind() == Demangle::Node::Kind::Identifier) { name = declNameNode->getText(); } else if (declNameNode->getKind() == Demangle::Node::Kind::PrivateDeclName) { name = declNameNode->getChild(1)->getText(); privateDiscriminator = Ctx.getIdentifier(declNameNode->getChild(0)->getText()); } else if (declNameNode->getKind() == Demangle::Node::Kind::RelatedEntityDeclName) { name = declNameNode->getChild(0)->getText(); relatedEntityKind = declNameNode->getText(); // Ignore any other decl-name productions for now. } else { return nullptr; } DeclContext *dc = findDeclContext(node->getChild(0)); if (!dc) { // Do some backup logic for foreign type declarations. if (privateDiscriminator.empty()) { if (auto foreignModuleKind = getForeignModuleKind(node->getChild(0))) { return findForeignNominalTypeDecl(name, relatedEntityKind, foreignModuleKind.getValue(), node->getKind()); } } return nullptr; } return findNominalTypeDecl(dc, Ctx.getIdentifier(name), privateDiscriminator, node->getKind()); } case Demangle::Node::Kind::Global: return findDeclContext(node->getChild(0)); // Bail out on other kinds of contexts. // TODO: extensions // TODO: local contexts default: return nullptr; } } NominalTypeDecl * RemoteASTTypeBuilder::findNominalTypeDecl(DeclContext *dc, Identifier name, Identifier privateDiscriminator, Demangle::Node::Kind kind) { auto module = dc->getParentModule(); SmallVector lookupResults; module->lookupMember(lookupResults, dc, name, privateDiscriminator); NominalTypeDecl *result = nullptr; for (auto decl : lookupResults) { // Ignore results that are not the right kind of nominal type declaration. NominalTypeDecl *candidate = getAcceptableNominalTypeCandidate(decl, kind); if (!candidate) continue; // Ignore results that aren't actually from the defining module. if (candidate->getParentModule() != module) continue; // This is a viable result. // If we already have a viable result, it's ambiguous, so give up. if (result) return nullptr; result = candidate; } return result; } static Optional getClangTypeKindForNodeKind(Demangle::Node::Kind kind) { switch (kind) { case Demangle::Node::Kind::Protocol: return ClangTypeKind::ObjCProtocol; case Demangle::Node::Kind::Class: return ClangTypeKind::ObjCClass; case Demangle::Node::Kind::TypeAlias: return ClangTypeKind::Typedef; case Demangle::Node::Kind::Structure: case Demangle::Node::Kind::Enum: return ClangTypeKind::Tag; default: return None; } } NominalTypeDecl * RemoteASTTypeBuilder::findForeignNominalTypeDecl(StringRef name, StringRef relatedEntityKind, ForeignModuleKind foreignKind, Demangle::Node::Kind kind) { // Check to see if we have an importer loaded. auto importer = static_cast(Ctx.getClangModuleLoader()); if (!importer) return nullptr; // Find the unique declaration that has the right kind. struct Consumer : VisibleDeclConsumer { Demangle::Node::Kind ExpectedKind; NominalTypeDecl *Result = nullptr; bool HadError = false; explicit Consumer(Demangle::Node::Kind kind) : ExpectedKind(kind) {} void foundDecl(ValueDecl *decl, DeclVisibilityKind reason) override { if (HadError) return; if (decl == Result) return; if (!Result) { Result = cast(decl); } else { HadError = true; Result = nullptr; } } } consumer(kind); switch (foreignKind) { case ForeignModuleKind::SynthesizedByImporter: if (!relatedEntityKind.empty()) { Optional lookupKind = getClangTypeKindForNodeKind(kind); if (!lookupKind) return nullptr; importer->lookupRelatedEntity(name, lookupKind.getValue(), relatedEntityKind, [&](TypeDecl *found) { consumer.foundDecl(found, DeclVisibilityKind::VisibleAtTopLevel); }); break; } importer->lookupValue(Ctx.getIdentifier(name), consumer); if (consumer.Result) consumer.Result = getAcceptableNominalTypeCandidate(consumer.Result,kind); break; case ForeignModuleKind::Imported: { Optional lookupKind = getClangTypeKindForNodeKind(kind); if (!lookupKind) return nullptr; importer->lookupTypeDecl(name, lookupKind.getValue(), [&](TypeDecl *found) { consumer.foundDecl(found, DeclVisibilityKind::VisibleAtTopLevel); }); } } return consumer.Result; } namespace { /// The basic implementation of the RemoteASTContext interface. /// The template subclasses do target-specific logic. class RemoteASTContextImpl { std::unique_ptr IRGen; public: RemoteASTContextImpl() = default; virtual ~RemoteASTContextImpl() = default; virtual Result getTypeForRemoteTypeMetadata(RemoteAddress metadata, bool skipArtificial) = 0; virtual Result getKindForRemoteTypeMetadata(RemoteAddress metadata) = 0; virtual Result getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) = 0; virtual Result getHeapMetadataForObject(RemoteAddress object) = 0; Result getOffsetOfMember(Type type, RemoteAddress optMetadata, StringRef memberName){ // Sanity check: obviously invalid arguments. if (!type || memberName.empty()) return Result::emplaceFailure(Failure::BadArgument); // Sanity check: if the caller gave us a dependent type, there's no way // we can handle that. if (type->hasTypeParameter() || type->hasArchetype()) return Result::emplaceFailure(Failure::DependentArgument); // Split into cases. if (auto typeDecl = type->getNominalOrBoundGenericNominal()) { return getOffsetOfField(type, typeDecl, optMetadata, memberName); } else if (auto tupleType = type->getAs()) { return getOffsetOfTupleElement(tupleType, optMetadata, memberName); } else { return Result::emplaceFailure(Failure::TypeHasNoSuchMember, memberName); } } protected: template Result getFailure() { return getBuilder().getFailureAsResult(Failure::Unknown); } template Result fail(KindTy kind, ArgTys &&...args) { return Result::emplaceFailure(kind, std::forward(args)...); } private: virtual RemoteASTTypeBuilder &getBuilder() = 0; virtual MemoryReader &getReader() = 0; virtual bool readWordOffset(RemoteAddress address, int64_t *offset) = 0; virtual std::unique_ptr createIRGenContext() = 0; virtual Result getOffsetOfTupleElementFromMetadata(RemoteAddress metadata, unsigned elementIndex) = 0; virtual Result getOffsetOfFieldFromMetadata(RemoteAddress metadata, StringRef memberName) = 0; IRGenContext *getIRGen() { if (!IRGen) IRGen = createIRGenContext(); return IRGen.get(); } Result getOffsetOfField(Type type, NominalTypeDecl *typeDecl, RemoteAddress optMetadata, StringRef memberName) { if (!isa(typeDecl) && !isa(typeDecl)) return fail(Failure::Unimplemented, "access members of this kind of type"); // Try to find the member. VarDecl *member = findField(typeDecl, memberName); // If we found a member, try to find its offset statically. if (member) { if (auto irgen = getIRGen()) { return getOffsetOfFieldFromIRGen(irgen->IGM, type, typeDecl, optMetadata, member); } } // Try searching the metadata for a member with the given name. if (optMetadata) { return getOffsetOfFieldFromMetadata(optMetadata, memberName); } // Okay, that's everything we know how to try. // Use a specialized diagnostic if we couldn't find any such member. if (!member) { return fail(Failure::TypeHasNoSuchMember, memberName); } return fail(Failure::Unknown); } /// Look for an instance property of the given nominal type that's /// known to be stored. VarDecl *findField(NominalTypeDecl *typeDecl, StringRef memberName) { for (auto field : typeDecl->getStoredProperties()) { if (field->getName().str() == memberName) return field; } return nullptr; } using MemberAccessStrategy = irgen::MemberAccessStrategy; Result getOffsetOfFieldFromIRGen(irgen::IRGenModule &IGM, Type type, NominalTypeDecl *typeDecl, RemoteAddress optMetadata, VarDecl *member) { SILType loweredTy = IGM.getSILTypes().getLoweredType(type); MemberAccessStrategy strategy = (isa(typeDecl) ? getPhysicalStructMemberAccessStrategy(IGM, loweredTy, member) : getPhysicalClassMemberAccessStrategy(IGM, loweredTy, member)); switch (strategy.getKind()) { case MemberAccessStrategy::Kind::Complex: return fail(Failure::Unimplemented, "access members with complex storage"); case MemberAccessStrategy::Kind::DirectFixed: return uint64_t(strategy.getDirectOffset().getValue()); case MemberAccessStrategy::Kind::DirectGlobal: { RemoteAddress directOffsetAddress = getReader().getSymbolAddress(strategy.getDirectGlobalSymbol()); if (!directOffsetAddress) return getFailure(); return readDirectOffset(directOffsetAddress, strategy.getDirectOffsetKind()); } case MemberAccessStrategy::Kind::IndirectFixed: { // We can't apply indirect offsets without metadata. if (!optMetadata) return fail(Failure::Unimplemented, "access generically-offset members without " "metadata"); Size indirectOffset = strategy.getIndirectOffset(); return readIndirectOffset(optMetadata, indirectOffset, strategy.getDirectOffsetKind()); } case MemberAccessStrategy::Kind::IndirectGlobal: { // We can't apply indirect offsets without metadata. if (!optMetadata) return fail(Failure::Unimplemented, "access generically-offset members without " "metadata"); RemoteAddress indirectOffsetAddress = getReader().getSymbolAddress(strategy.getIndirectGlobalSymbol()); Size indirectOffset; if (!readOffset(indirectOffsetAddress, strategy.getIndirectOffsetKind(), indirectOffset)) return getFailure(); return readIndirectOffset(optMetadata, indirectOffset, strategy.getDirectOffsetKind()); } } llvm_unreachable("bad member MemberAccessStrategy"); } bool readOffset(RemoteAddress address, MemberAccessStrategy::OffsetKind kind, Size &offset) { switch (kind) { case MemberAccessStrategy::OffsetKind::Bytes_Word: { int64_t rawOffset; if (!readWordOffset(address, &rawOffset)) return false; offset = Size(rawOffset); return true; } } llvm_unreachable("bad offset kind"); } Result readIndirectOffset(RemoteAddress metadata, Size indirectOffset, MemberAccessStrategy::OffsetKind kind) { RemoteAddress directOffsetAddress = metadata + indirectOffset; return readDirectOffset(directOffsetAddress, kind); } Result readDirectOffset(RemoteAddress directOffsetAddress, MemberAccessStrategy::OffsetKind kind) { Size directOffset; if (!readOffset(directOffsetAddress, kind, directOffset)) return getFailure(); return uint64_t(directOffset.getValue()); } /// Read the Result getOffsetOfTupleElement(TupleType *type, RemoteAddress optMetadata, StringRef memberName) { // Check that the member "name" is a valid index into the tuple. unsigned targetIndex; if (memberName.getAsInteger(10, targetIndex) || targetIndex >= type->getNumElements()) return fail(Failure::TypeHasNoSuchMember, memberName); // Fast path: element 0 is always at offset 0. if (targetIndex == 0) return uint64_t(0); // Create an IRGen instance. auto irgen = getIRGen(); if (!irgen) return Result::emplaceFailure(Failure::Unknown); auto &IGM = irgen->IGM; SILType loweredTy = IGM.getSILTypes().getLoweredType(type); // If the type has a statically fixed offset, return that. if (auto offset = irgen::getFixedTupleElementOffset(IGM, loweredTy, targetIndex)) return offset->getValue(); // If we have metadata, go load from that. if (optMetadata) return getOffsetOfTupleElementFromMetadata(optMetadata, targetIndex); // Okay, reproduce tuple layout. // Find the last element with a known offset. Note that we don't // have to ask IRGen about element 0 because we know its size is zero. Size lastOffset = Size(0); unsigned lastIndex = targetIndex; for (--lastIndex; lastIndex != 0; --lastIndex) { if (auto offset = irgen::getFixedTupleElementOffset(IGM, loweredTy, lastIndex)) { lastOffset = *offset; break; } } // Okay, iteratively build up from there. for (; ; ++lastIndex) { // Try to get the size and alignment of this element. SILType eltTy = loweredTy.getTupleElementType(lastIndex); auto sizeAndAlignment = getTypeSizeAndAlignment(IGM, eltTy); if (!sizeAndAlignment) return getFailure(); // Round up to the alignment of the element. lastOffset = lastOffset.roundUpToAlignment(sizeAndAlignment->second); // If this is the target, we're done. if (lastIndex == targetIndex) return lastOffset.getValue(); // Otherwise, skip forward by the size of the element. lastOffset += sizeAndAlignment->first; } llvm_unreachable("didn't reach target index"); } /// Attempt to discover the size and alignment of the given type. Optional> getTypeSizeAndAlignment(irgen::IRGenModule &IGM, SILType eltTy) { auto &eltTI = IGM.getTypeInfo(eltTy); if (auto fixedTI = dyn_cast(&eltTI)) { return std::make_pair(fixedTI->getFixedSize(), fixedTI->getFixedAlignment()); } // TODO: handle resilient types return None; } }; /// A template for generating target-specific implementations of the /// RemoteASTContext interface. template class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl { MetadataReader Reader; RemoteASTTypeBuilder &getBuilder() override { return Reader.Builder; } MemoryReader &getReader() override { return *Reader.Reader; } bool readWordOffset(RemoteAddress address, int64_t *extendedOffset) override { using unsigned_size_t = typename Runtime::StoredSize; using signed_size_t = typename std::make_signed::type; signed_size_t offset; if (!getReader().readInteger(address, &offset)) return false; *extendedOffset = offset; return true; } public: RemoteASTContextConcreteImpl(std::shared_ptr &&reader, ASTContext &ctx) : Reader(std::move(reader), ctx) {} Result getTypeForRemoteTypeMetadata(RemoteAddress metadata, bool skipArtificial) override { if (auto result = Reader.readTypeFromMetadata(metadata.getAddressData(), skipArtificial)) return result; return getFailure(); } Result getKindForRemoteTypeMetadata(RemoteAddress metadata) override { auto result = Reader.readKindFromMetadata(metadata.getAddressData()); if (result) return *result; return getFailure(); } Result getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) override { if (auto result = Reader.readNominalTypeFromDescriptor(descriptor.getAddressData())) return result; return getFailure(); } std::unique_ptr createIRGenContext() override { return getBuilder().createIRGenContext(); } Result getOffsetOfTupleElementFromMetadata(RemoteAddress metadata, unsigned index) override { typename Runtime::StoredSize offset; if (Reader.readTupleElementOffset(metadata.getAddressData(), index, &offset)) return uint64_t(offset); return getFailure(); } Result getOffsetOfFieldFromMetadata(RemoteAddress metadata, StringRef memberName) override { // TODO: this would be useful for resilience return fail(Failure::Unimplemented, "look up field offset by name"); } Result getHeapMetadataForObject(RemoteAddress object) override { auto result = Reader.readMetadataFromInstance(object.getAddressData()); if (result) return RemoteAddress(*result); return getFailure(); } }; } // end anonymous namespace static RemoteASTContextImpl *createImpl(ASTContext &ctx, std::shared_ptr &&reader) { auto &target = ctx.LangOpts.Target; assert(target.isArch32Bit() || target.isArch64Bit()); if (target.isArch32Bit()) { using Target = External>; return new RemoteASTContextConcreteImpl(std::move(reader), ctx); } else { using Target = External>; return new RemoteASTContextConcreteImpl(std::move(reader), ctx); } } static RemoteASTContextImpl *asImpl(void *impl) { return static_cast(impl); } RemoteASTContext::RemoteASTContext(ASTContext &ctx, std::shared_ptr reader) : Impl(createImpl(ctx, std::move(reader))) { } RemoteASTContext::~RemoteASTContext() { delete asImpl(Impl); } Result RemoteASTContext::getTypeForRemoteTypeMetadata(RemoteAddress address, bool skipArtificial) { return asImpl(Impl)->getTypeForRemoteTypeMetadata(address, skipArtificial); } Result RemoteASTContext::getKindForRemoteTypeMetadata(remote::RemoteAddress address) { return asImpl(Impl)->getKindForRemoteTypeMetadata(address); } Result RemoteASTContext::getDeclForRemoteNominalTypeDescriptor(RemoteAddress address) { return asImpl(Impl)->getDeclForRemoteNominalTypeDescriptor(address); } Result RemoteASTContext::getOffsetOfMember(Type type, RemoteAddress optMetadata, StringRef memberName) { return asImpl(Impl)->getOffsetOfMember(type, optMetadata, memberName); } Result RemoteASTContext::getHeapMetadataForObject(remote::RemoteAddress address) { return asImpl(Impl)->getHeapMetadataForObject(address); }