Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reuse reflection code from the Swift toolchain #384

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
--funcattributes prev-line
--disable andOperator
--swiftversion 5.3
--exclude Tests/TokamakTests/EnumerateFieldsTests.swift
10 changes: 8 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,14 @@ let package = Package(
condition: .when(platforms: [.wasi, .linux])
)]
),
.target(
name: "CReflection",
exclude: ["swift/ABI/MetadataKind.def", "swift/ABI/ValueWitness.def"],
cxxSettings: [.headerSearchPath(".")]
),
.target(
name: "TokamakCore",
dependencies: ["CombineShim"]
dependencies: ["CombineShim", "CReflection"]
),
.target(
name: "TokamakShim",
Expand Down Expand Up @@ -181,5 +186,6 @@ let package = Package(
// name: "TokamakStaticHTMLTests",
// dependencies: ["TokamakStaticHTML"]
// ),
]
],
cxxLanguageStandard: .cxx14
)
110 changes: 110 additions & 0 deletions Sources/CReflection/CReflection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// MIT License
//
// Copyright (c) 2019 Sergej Jaskiewicz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by Sergej Jaskiewicz on 25.10.2019.
//

#include "CReflection.h"
#include "swift/ABI/Metadata.h"
#include "swift/Runtime/Metadata.h"
#include "swift/Reflection/Records.h"
#include "stl_polyfill/string_view.h"

using namespace tokamak;
using namespace swift;
using namespace reflection;

// This function is defined in the Swift runtime.
OPENCOMBINE_SWIFT_CALLING_CONVENTION
extern "C" const Metadata *
swift_getTypeByMangledNameInContext(const char *typeNameStart,
size_t typeNameLength,
const ContextDescriptor *context,
const Metadata *const *genericArgs);

namespace
{
const Metadata *getTypeMetadata(const FieldRecord &record,
const Metadata *fieldOwner)
{
string_view mangledTypeName = record.getMangledTypeName(0);
return swift_getTypeByMangledNameInContext(mangledTypeName.data(),
mangledTypeName.size(),
fieldOwner->getTypeContextDescriptor(),
fieldOwner->getGenericArgs());
}

string_view nextTupleLabel(const char *&labels)
{
const char *start = labels;
while (true)
{
char current = *labels++;
if (current == ' ' || current == '\0')
{
break;
}
}
return {start, size_t(labels - start - 1)};
}

} // end anonymous namespace

size_t tokamak_get_size(const void *opaqueMetadataPtr) {
const Metadata *metadata = static_cast<const Metadata *>(opaqueMetadataPtr);

return metadata->vw_size();
}

bool tokamak_enumerate_fields(const void *opaqueMetadataPtr,
bool allowResilientSuperclasses,
void *enumeratorContext,
TokamakFieldEnumerator enumerator)
{
auto enumerateFields = [&](const auto *metadata,
const TypeContextDescriptor *description) -> bool {
const auto *fieldOffsets = metadata->getFieldOffsets();
const FieldDescriptor &fieldDescriptor = *description->Fields;

for (const FieldRecord &fieldRecord : fieldDescriptor)
{
if (!enumerator(enumeratorContext,
fieldRecord.getFieldName(0).data(),
*fieldOffsets++,
getTypeMetadata(fieldRecord, metadata)))
{
return false;
}
}

return true;
};

const Metadata *metadata = static_cast<const Metadata *>(opaqueMetadataPtr);

if (const auto *structMetadata = llvm::dyn_cast<StructMetadata>(metadata))
{
return enumerateFields(structMetadata, structMetadata->getDescription());
}

return false;
}
42 changes: 42 additions & 0 deletions Sources/CReflection/Demangler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===--- Demangler.cpp - String to Node-Tree Demangling -------------------===//
//
// 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 new Swift de-mangler.
//
//===----------------------------------------------------------------------===//

// MODIFICATION NOTE:
// This file has been modified for the OpenCombine open source project.
// - Some functions have been removed.
// - The swift namespace is wrapped in the opencombine namespace.

#include "swift/Demangling/Demangle.h"

using namespace tokamak;
using namespace swift;

string_view
swift::Demangle::makeSymbolicMangledNameStringRef(const char *base) {
if (!base)
return {};

auto end = base;
while (*end != '\0') {
// Skip over symbolic references.
if (*end >= '\x01' && *end <= '\x17')
end += sizeof(uint32_t);
else if (*end >= '\x18' && *end <= '\x1F')
end += sizeof(void*);
++end;
}
return { base, size_t(end - base) };
}
123 changes: 123 additions & 0 deletions Sources/CReflection/Metadata.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//===--- Metadata.cpp - Swift Language ABI Metadata Support ---------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Implementations of the metadata ABI functions.
//
//===----------------------------------------------------------------------===//

// MODIFICATION NOTE:
// This file has been modified for the OpenCombine open source project.
// - Some functions have been removed.
// - The swift namespace is wrapped in the opencombine namespace.

#include "swift/Runtime/Metadata.h"

using namespace tokamak;
using namespace swift;

#if OPENCOMBINE_SWIFT_OBJC_INTEROP
static ClassMetadataBounds computeMetadataBoundsForObjCClass(Class cls) {
cls = swift_getInitializedObjCClass(cls);
auto metadata = reinterpret_cast<const ClassMetadata *>(cls);
return metadata->getClassBoundsAsSwiftSuperclass();
}
#endif

static ClassMetadataBounds
computeMetadataBoundsForSuperclass(const void *ref,
TypeReferenceKind refKind) {
switch (refKind) {
case TypeReferenceKind::IndirectTypeDescriptor: {
auto description = *reinterpret_cast<const ClassDescriptor * const *>(ref);
if (!description) {
// swift::fatalError(0, "instantiating class metadata for class with "
// "missing weak-linked ancestor");
abort();
}
return description->getMetadataBounds();
}

case TypeReferenceKind::DirectTypeDescriptor: {
auto description = reinterpret_cast<const ClassDescriptor *>(ref);
return description->getMetadataBounds();
}

case TypeReferenceKind::DirectObjCClassName: {
#if OPENCOMBINE_SWIFT_OBJC_INTEROP
auto cls = objc_lookUpClass(reinterpret_cast<const char *>(ref));
return computeMetadataBoundsForObjCClass(cls);
#else
break;
#endif
}

case TypeReferenceKind::IndirectObjCClass: {
#if OPENCOMBINE_SWIFT_OBJC_INTEROP
auto cls = *reinterpret_cast<const Class *>(ref);
return computeMetadataBoundsForObjCClass(cls);
#else
break;
#endif
}
}
opencombine_swift_runtime_unreachable("unsupported superclass reference kind");
}

static ClassMetadataBounds computeMetadataBoundsFromSuperclass(
const ClassDescriptor *description,
StoredClassMetadataBounds &storedBounds) {
ClassMetadataBounds bounds;

// Compute the bounds for the superclass, extending it to the minimum
// bounds of a Swift class.
if (const void *superRef = description->getResilientSuperclass()) {
bounds = computeMetadataBoundsForSuperclass(superRef,
description->getResilientSuperclassReferenceKind());
} else {
bounds = ClassMetadataBounds::forSwiftRootClass();
}

// Add the subclass's immediate members.
bounds.adjustForSubclass(description->areImmediateMembersNegative(),
description->NumImmediateMembers);

// Cache before returning.
storedBounds.initialize(bounds);
return bounds;
}

ClassMetadataBounds
swift::getResilientMetadataBounds(const ClassDescriptor *description) {
assert(description->hasResilientSuperclass());
auto &storedBounds = *description->ResilientMetadataBounds.get();

ClassMetadataBounds bounds;
if (storedBounds.tryGet(bounds)) {
return bounds;
}

return computeMetadataBoundsFromSuperclass(description, storedBounds);
}

int32_t
swift::getResilientImmediateMembersOffset(const ClassDescriptor *description) {
assert(description->hasResilientSuperclass());
auto &storedBounds = *description->ResilientMetadataBounds.get();

ptrdiff_t result;
if (storedBounds.tryGetImmediateMembersOffset(result)) {
return result / sizeof(void*);
}

auto bounds = computeMetadataBoundsFromSuperclass(description, storedBounds);
return bounds.ImmediateMembersOffset / sizeof(void*);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// MIT License
//
// Copyright (c) 2017-2021 Wesley Wickwire and Tokamak contributors
// Copyright (c) 2019 Sergej Jaskiewicz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
Expand All @@ -19,13 +19,47 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Created by Sergej Jaskiewicz on 23/09/2019.
//

#ifndef CREFLECTION_H
#define CREFLECTION_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

#if __has_attribute(swift_name)
#define TOKAMAK_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
#else
#define TOKAMAK_SWIFT_NAME(_name)
#endif

#ifdef __cplusplus
extern "C"
{
#endif

#pragma mark - Type metadata

typedef bool (*_Nonnull TokamakFieldEnumerator)(
void *_Nullable enumeratorContext,
const char *_Nonnull fieldName,
size_t fieldOffset,
const void *_Nonnull fieldTypeMetadataPtr);

func metadataPointer(type: Any.Type) -> UnsafePointer<Int> {
unsafeBitCast(type, to: UnsafePointer<Int>.self)
}
bool
tokamak_enumerate_fields(
const void *_Nonnull type_metadata,
bool allowResilientSuperclasses,
void *_Nullable enumerator_context,
TokamakFieldEnumerator enumerator
) TOKAMAK_SWIFT_NAME(enumerateFields(typeMetadata:allowResilientSuperclasses:enumeratorContext:enumerator:));

func metadata(of type: Any.Type) -> StructMetadata {
guard Kind(type: type) == .struct else { fatalError("Reflection is supported only for structs") }
size_t tokamak_get_size(const void *_Nonnull opaqueMetadataPtr);
#ifdef __cplusplus
} // extern "C"
#endif

return StructMetadata(type: type)
}
#endif /* CREFLECTION_H */
Loading