mirror of
https://github.com/apple/pkl.git
synced 2026-01-11 22:30:54 +01:00
Expose collected superclass properties/methods in pkl:reflect (#1106)
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
package org.pkl.core.ast.member;
|
||||
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.pkl.core.Member.SourceLocation;
|
||||
import org.pkl.core.PClass;
|
||||
@@ -67,8 +68,44 @@ public final class ClassProperty extends ClassMember {
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
public VmTyped getMirror() {
|
||||
return MirrorFactories.propertyFactory.create(this);
|
||||
public static final class Mirror {
|
||||
private final ClassProperty prop;
|
||||
private final VmClass clazz;
|
||||
|
||||
Mirror(ClassProperty prop, VmClass clazz) {
|
||||
this.prop = prop;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public ClassProperty getProperty() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public List<VmTyped> getAllAnnotations() {
|
||||
var annotations = new ArrayList<VmTyped>();
|
||||
for (var klazz = clazz; klazz != null; klazz = klazz.getSuperclass()) {
|
||||
var p = klazz.getDeclaredProperty(prop.getName());
|
||||
if (p != null) {
|
||||
annotations.addAll(p.getAnnotations());
|
||||
}
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public VmSet getAllModifierMirrors() {
|
||||
var mods = 0;
|
||||
for (var klazz = clazz; klazz != null; klazz = klazz.getSuperclass()) {
|
||||
var parent = klazz.getDeclaredProperty(prop.getName());
|
||||
if (parent != null) {
|
||||
mods |= parent.getModifiers();
|
||||
}
|
||||
}
|
||||
return VmModifier.getMirrors(mods, false);
|
||||
}
|
||||
}
|
||||
|
||||
public VmTyped getMirror(VmClass clazz) {
|
||||
return MirrorFactories.propertyFactory.create(new Mirror(this, clazz));
|
||||
}
|
||||
|
||||
public VmSet getModifierMirrors() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,7 @@ public final class MirrorFactories {
|
||||
public static final VmObjectFactory<VmTypeAlias> typeAliasFactory =
|
||||
new VmObjectFactory<>(ReflectModule::getTypeAliasClass);
|
||||
|
||||
public static final VmObjectFactory<ClassProperty> propertyFactory =
|
||||
public static final VmObjectFactory<ClassProperty.Mirror> propertyFactory =
|
||||
new VmObjectFactory<>(ReflectModule::getPropertyClass);
|
||||
|
||||
public static final VmObjectFactory<ClassMethod> methodFactory =
|
||||
@@ -146,8 +146,10 @@ public final class MirrorFactories {
|
||||
.addProperty("superclass", VmClass::getSuperclassMirror)
|
||||
.addProperty("supertype", VmClass::getSupertypeMirror)
|
||||
.addMapProperty("properties", VmClass::getPropertyMirrors)
|
||||
.addMapProperty("allProperties", VmClass::getAllPropertyMirrors)
|
||||
.addTypedProperty("enclosingDeclaration", VmClass::getModuleMirror)
|
||||
.addMapProperty("methods", VmClass::getMethodMirrors);
|
||||
.addMapProperty("methods", VmClass::getMethodMirrors)
|
||||
.addMapProperty("allMethods", VmClass::getAllMethodMirrors);
|
||||
|
||||
typeAliasFactory
|
||||
.addTypedProperty(
|
||||
@@ -164,24 +166,30 @@ public final class MirrorFactories {
|
||||
|
||||
propertyFactory
|
||||
.addTypedProperty(
|
||||
"location", property -> sourceLocationFactory.create(property.getHeaderSection()))
|
||||
"location",
|
||||
property -> sourceLocationFactory.create(property.getProperty().getHeaderSection()))
|
||||
.addProperty(
|
||||
"docComment",
|
||||
property -> VmNull.lift(VmUtils.exportDocComment(property.getDocComment())))
|
||||
.addListProperty("annotations", property -> VmList.create(property.getAnnotations()))
|
||||
.addSetProperty("modifiers", ClassProperty::getModifierMirrors)
|
||||
.addStringProperty("name", property -> property.getName().toString())
|
||||
.addTypedProperty("type", ClassProperty::getTypeMirror)
|
||||
property ->
|
||||
VmNull.lift(VmUtils.exportDocComment(property.getProperty().getDocComment())))
|
||||
.addListProperty(
|
||||
"annotations", property -> VmList.create(property.getProperty().getAnnotations()))
|
||||
.addListProperty("allAnnotations", property -> VmList.create(property.getAllAnnotations()))
|
||||
.addSetProperty("modifiers", property -> property.getProperty().getModifierMirrors())
|
||||
.addSetProperty("allModifiers", ClassProperty.Mirror::getAllModifierMirrors)
|
||||
.addStringProperty("name", property -> property.getProperty().getName().toString())
|
||||
.addTypedProperty("type", property -> property.getProperty().getTypeMirror())
|
||||
.addProperty(
|
||||
"defaultValue",
|
||||
property ->
|
||||
property.isAbstract()
|
||||
|| property.isExternal()
|
||||
|| property.getInitializer().isUndefined()
|
||||
property.getProperty().isAbstract()
|
||||
|| property.getProperty().isExternal()
|
||||
|| property.getProperty().getInitializer().isUndefined()
|
||||
? VmNull.withoutDefault()
|
||||
:
|
||||
// get default from prototype because it's cached there
|
||||
VmUtils.readMember(property.getOwner(), property.getName()));
|
||||
VmUtils.readMember(
|
||||
property.getProperty().getOwner(), property.getProperty().getName()));
|
||||
|
||||
methodFactory
|
||||
.addTypedProperty(
|
||||
|
||||
@@ -575,7 +575,16 @@ public final class VmClass extends VmValue {
|
||||
var builder = VmMap.builder();
|
||||
for (var property : declaredProperties.getValues()) {
|
||||
if (property.isLocal()) continue;
|
||||
builder.add(property.getName().toString(), property.getMirror());
|
||||
builder.add(property.getName().toString(), property.getMirror(this));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public VmMap getAllPropertyMirrors() {
|
||||
var builder = VmMap.builder();
|
||||
for (var property : getAllProperties().getValues()) {
|
||||
if (property.isLocal()) continue;
|
||||
builder.add(property.getName().toString(), property.getMirror(this));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
@@ -589,6 +598,15 @@ public final class VmClass extends VmValue {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public VmMap getAllMethodMirrors() {
|
||||
var builder = VmMap.builder();
|
||||
for (var method : getAllMethods().getValues()) {
|
||||
if (method.isLocal()) continue;
|
||||
builder.add(method.getName().toString(), method.getMirror());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public PClass export() {
|
||||
|
||||
88
pkl-core/src/test/files/LanguageSnippetTests/input/api/reflect4.pkl
vendored
Normal file
88
pkl-core/src/test/files/LanguageSnippetTests/input/api/reflect4.pkl
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
amends ".../snippetTest.pkl"
|
||||
|
||||
import "pkl:reflect"
|
||||
|
||||
local class MyAnn extends Annotation
|
||||
|
||||
open local class A {
|
||||
inheritedFromA: String
|
||||
overriddenInBFromA: String
|
||||
overriddenInCFromA: String
|
||||
overriddenInBCFromA: String
|
||||
|
||||
fixed modA: String = ""
|
||||
fixed modAOverrideB: String = ""
|
||||
fixed modAOverrideC: String = ""
|
||||
fixed modAOverrideBC: String = ""
|
||||
fixed modAOverrideCB: String = ""
|
||||
|
||||
@Unlisted
|
||||
annA: String
|
||||
@Unlisted
|
||||
annAOverrideB: String
|
||||
@Unlisted
|
||||
annAOverrideC: String
|
||||
@Unlisted
|
||||
annAOverrideBC: String
|
||||
}
|
||||
|
||||
open local class B extends A {
|
||||
inheritedFromB: String
|
||||
overriddenInBFromA: Int
|
||||
overriddenInCFromB: String
|
||||
overriddenInBCFromA: Int
|
||||
|
||||
fixed modB: String = ""
|
||||
hidden fixed modAOverrideB: Int = 0
|
||||
hidden fixed modAOverrideBC: Int = 0
|
||||
fixed modAOverrideCB: Int = 0
|
||||
|
||||
@Unlisted
|
||||
annB: String
|
||||
@MyAnn
|
||||
annAOverrideB: String
|
||||
@MyAnn
|
||||
annAOverrideBC: String
|
||||
}
|
||||
|
||||
local class C extends B {
|
||||
fromC: String
|
||||
overriddenInCFromA: Int
|
||||
overriddenInCFromB: Int
|
||||
overriddenInBCFromA: Boolean
|
||||
|
||||
fixed modC: String = ""
|
||||
hidden fixed modAOverrideC: Int = 0
|
||||
fixed modAOverrideBC: Boolean = false
|
||||
hidden fixed modAOverrideCB: Boolean = false
|
||||
|
||||
@Unlisted
|
||||
annC: String
|
||||
@MyAnn
|
||||
annAOverrideC: String
|
||||
@AlsoKnownAs { names { "mergedAnnotations" } }
|
||||
annAOverrideBC: String
|
||||
}
|
||||
|
||||
local class DisplayProperty {
|
||||
hidden property: reflect.Property
|
||||
fixed name = property.name
|
||||
fixed annotations = property.annotations.map((it) -> it.toString()).toListing()
|
||||
fixed allAnnotations = property.allAnnotations.map((it) -> it.toString()).toListing()
|
||||
fixed modifiers = property.modifiers.toListing()
|
||||
fixed allModifiers = property.allModifiers.toListing()
|
||||
fixed type = property.type.getPropertyOrNull("referent")?.getPropertyOrNull("name") ?? "unknown"
|
||||
}
|
||||
|
||||
examples {
|
||||
["C"] {
|
||||
reflect.Class(C).allProperties.mapValues((_, it) -> new DisplayProperty { property = it }).toMapping()
|
||||
}
|
||||
["module"] {
|
||||
new DisplayProperty { property = reflect.Class(module.getClass()).properties["output"] }
|
||||
}
|
||||
}
|
||||
|
||||
output { // override to test example "module", allModifiers should contain "hidden"
|
||||
text = super.text + "\n// hello world"
|
||||
}
|
||||
243
pkl-core/src/test/files/LanguageSnippetTests/output/api/reflect4.pcf
vendored
Normal file
243
pkl-core/src/test/files/LanguageSnippetTests/output/api/reflect4.pcf
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
examples {
|
||||
["C"] {
|
||||
new {
|
||||
["inheritedFromA"] {
|
||||
name = "inheritedFromA"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["overriddenInBFromA"] {
|
||||
name = "overriddenInBFromA"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "Int"
|
||||
}
|
||||
["overriddenInCFromA"] {
|
||||
name = "overriddenInCFromA"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "Int"
|
||||
}
|
||||
["overriddenInBCFromA"] {
|
||||
name = "overriddenInBCFromA"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "Boolean"
|
||||
}
|
||||
["modA"] {
|
||||
name = "modA"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"fixed"
|
||||
}
|
||||
type = "String"
|
||||
}
|
||||
["modAOverrideB"] {
|
||||
name = "modAOverrideB"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
type = "Int"
|
||||
}
|
||||
["modAOverrideC"] {
|
||||
name = "modAOverrideC"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
type = "Int"
|
||||
}
|
||||
["modAOverrideBC"] {
|
||||
name = "modAOverrideBC"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
type = "Boolean"
|
||||
}
|
||||
["modAOverrideCB"] {
|
||||
name = "modAOverrideCB"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"hidden"
|
||||
"fixed"
|
||||
}
|
||||
type = "Boolean"
|
||||
}
|
||||
["annA"] {
|
||||
name = "annA"
|
||||
annotations {
|
||||
"new Unlisted {}"
|
||||
}
|
||||
allAnnotations {
|
||||
"new Unlisted {}"
|
||||
}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["annAOverrideB"] {
|
||||
name = "annAOverrideB"
|
||||
annotations {
|
||||
"new MyAnn {}"
|
||||
}
|
||||
allAnnotations {
|
||||
"new MyAnn {}"
|
||||
"new Unlisted {}"
|
||||
}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["annAOverrideC"] {
|
||||
name = "annAOverrideC"
|
||||
annotations {
|
||||
"new MyAnn {}"
|
||||
}
|
||||
allAnnotations {
|
||||
"new MyAnn {}"
|
||||
"new Unlisted {}"
|
||||
}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["annAOverrideBC"] {
|
||||
name = "annAOverrideBC"
|
||||
annotations {
|
||||
"new AlsoKnownAs { names { \"mergedAnnotations\" } }"
|
||||
}
|
||||
allAnnotations {
|
||||
"new AlsoKnownAs { names { \"mergedAnnotations\" } }"
|
||||
"new MyAnn {}"
|
||||
"new Unlisted {}"
|
||||
}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["inheritedFromB"] {
|
||||
name = "inheritedFromB"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["overriddenInCFromB"] {
|
||||
name = "overriddenInCFromB"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "Int"
|
||||
}
|
||||
["modB"] {
|
||||
name = "modB"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"fixed"
|
||||
}
|
||||
type = "String"
|
||||
}
|
||||
["annB"] {
|
||||
name = "annB"
|
||||
annotations {
|
||||
"new Unlisted {}"
|
||||
}
|
||||
allAnnotations {
|
||||
"new Unlisted {}"
|
||||
}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["fromC"] {
|
||||
name = "fromC"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
["modC"] {
|
||||
name = "modC"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {
|
||||
"fixed"
|
||||
}
|
||||
allModifiers {
|
||||
"fixed"
|
||||
}
|
||||
type = "String"
|
||||
}
|
||||
["annC"] {
|
||||
name = "annC"
|
||||
annotations {
|
||||
"new Unlisted {}"
|
||||
}
|
||||
allAnnotations {
|
||||
"new Unlisted {}"
|
||||
}
|
||||
modifiers {}
|
||||
allModifiers {}
|
||||
type = "String"
|
||||
}
|
||||
}
|
||||
}
|
||||
["module"] {
|
||||
new {
|
||||
name = "output"
|
||||
annotations {}
|
||||
allAnnotations {}
|
||||
modifiers {}
|
||||
allModifiers {
|
||||
"hidden"
|
||||
}
|
||||
type = "unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hello world
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user