mirror of
https://github.com/apple/pkl.git
synced 2026-06-25 06:46:18 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a6b02692bb |
@@ -24,7 +24,7 @@ import org.pkl.core.ast.VmModifier;
|
|||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
|
|
||||||
public final class ClassProperty extends ClassMember {
|
public final class ClassProperty extends ClassMember {
|
||||||
private final @Nullable PropertyTypeNode typeNode;
|
private @Nullable PropertyTypeNode typeNode;
|
||||||
private final ObjectMember initializer;
|
private final ObjectMember initializer;
|
||||||
|
|
||||||
public ClassProperty(
|
public ClassProperty(
|
||||||
@@ -64,6 +64,11 @@ public final class ClassProperty extends ClassMember {
|
|||||||
return VmModifier.getMirrors(mods, false);
|
return VmModifier.getMirrors(mods, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void lateInitTypeNodeAndModifiers(@Nullable PropertyTypeNode typeNode, int modifiers) {
|
||||||
|
this.typeNode = typeNode;
|
||||||
|
this.modifiers = modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable PropertyTypeNode getTypeNode() {
|
public @Nullable PropertyTypeNode getTypeNode() {
|
||||||
return typeNode;
|
return typeNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public abstract class Member {
|
|||||||
|
|
||||||
protected final SourceSection headerSection;
|
protected final SourceSection headerSection;
|
||||||
|
|
||||||
protected final int modifiers;
|
protected int modifiers;
|
||||||
|
|
||||||
protected final @Nullable Identifier name;
|
protected final @Nullable Identifier name;
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.dsl.Idempotent;
|
|||||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
import org.graalvm.collections.*;
|
import org.graalvm.collections.*;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
@@ -84,6 +85,13 @@ public final class VmClass extends VmValue {
|
|||||||
|
|
||||||
private final Object allHiddenPropertyNamesLock = new Object();
|
private final Object allHiddenPropertyNamesLock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("finalizersLock")
|
||||||
|
private @Nullable List<Runnable> __finalizers = null;
|
||||||
|
|
||||||
|
private final Object finalizersLock = new Object();
|
||||||
|
|
||||||
|
private final AtomicInteger uninitializedSuperclassCount = new AtomicInteger(0);
|
||||||
|
|
||||||
// Helps to overcome recursive initialization issues
|
// Helps to overcome recursive initialization issues
|
||||||
// between classes and annotations in pkl.base.
|
// between classes and annotations in pkl.base.
|
||||||
@CompilationFinal private volatile boolean isInitialized;
|
@CompilationFinal private volatile boolean isInitialized;
|
||||||
@@ -150,6 +158,83 @@ public final class VmClass extends VmValue {
|
|||||||
prototype.lateInitParent(superclass.getPrototype());
|
prototype.lateInitParent(superclass.getPrototype());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
private void checkAbstractMembers() {
|
||||||
|
if (this.isAbstract()) return;
|
||||||
|
// minimize allocations in the non-error case
|
||||||
|
if (!hasAbstractMember()) return;
|
||||||
|
var abstractMembers = getAbstractMembers();
|
||||||
|
if (abstractMembers.size() == 1) {
|
||||||
|
var member = abstractMembers.get(0);
|
||||||
|
var message =
|
||||||
|
member instanceof ClassProperty
|
||||||
|
? "noImplementationForAbstractProperty"
|
||||||
|
: "noImplementationForAbstractMethod";
|
||||||
|
throw new VmExceptionBuilder()
|
||||||
|
.evalError(message, getDisplayName(), member.getName().toString())
|
||||||
|
.withSourceSection(getHeaderSection())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
var memberList = new StringBuilder();
|
||||||
|
var isFirst = true;
|
||||||
|
for (var member : abstractMembers) {
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
} else {
|
||||||
|
memberList.append('\n');
|
||||||
|
}
|
||||||
|
memberList.append("* ");
|
||||||
|
if (member instanceof ClassProperty) {
|
||||||
|
memberList.append("property ");
|
||||||
|
} else {
|
||||||
|
memberList.append("method ");
|
||||||
|
}
|
||||||
|
memberList.append('`').append(member.getName()).append('`');
|
||||||
|
}
|
||||||
|
throw new VmExceptionBuilder()
|
||||||
|
.evalError("noImplementationForAbstractMembers", getDisplayName(), memberList.toString())
|
||||||
|
.withSourceSection(getHeaderSection())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAbstractMember() {
|
||||||
|
var propertyCursor = getAllProperties().getEntries();
|
||||||
|
while (propertyCursor.advance()) {
|
||||||
|
var property = propertyCursor.getValue();
|
||||||
|
if (property.isAbstract()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var methodCursor = getAllMethods().getEntries();
|
||||||
|
while (methodCursor.advance()) {
|
||||||
|
var method = methodCursor.getValue();
|
||||||
|
if (method.isAbstract()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ClassMember> getAbstractMembers() {
|
||||||
|
assert this.superclass != null;
|
||||||
|
var result = new ArrayList<ClassMember>();
|
||||||
|
var propertyCursor = getAllProperties().getEntries();
|
||||||
|
while (propertyCursor.advance()) {
|
||||||
|
var property = propertyCursor.getValue();
|
||||||
|
if (property.isAbstract()) {
|
||||||
|
result.add(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var methodCursor = getAllMethods().getEntries();
|
||||||
|
while (methodCursor.advance()) {
|
||||||
|
var method = methodCursor.getValue();
|
||||||
|
if (method.isAbstract()) {
|
||||||
|
result.add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public void addProperty(ClassProperty property) {
|
public void addProperty(ClassProperty property) {
|
||||||
prototype.addProperty(property.getInitializer());
|
prototype.addProperty(property.getInitializer());
|
||||||
@@ -190,8 +275,46 @@ public final class VmClass extends VmValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onInitialized(Runnable runnable) {
|
||||||
|
synchronized (finalizersLock) {
|
||||||
|
if (this.__finalizers == null) {
|
||||||
|
this.__finalizers = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.__finalizers.add(runnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Superclasses may not have finished their initialization when this method is called.
|
// Note: Superclasses may not have finished their initialization when this method is called.
|
||||||
public void notifyInitialized() {
|
public void notifyInitialized() {
|
||||||
|
var sc = superclass;
|
||||||
|
var isAllInitialized = true;
|
||||||
|
var uninitializedCount = 0;
|
||||||
|
while (sc != null) {
|
||||||
|
if (!sc.isInitialized) {
|
||||||
|
sc.onInitialized(
|
||||||
|
() -> {
|
||||||
|
var count = uninitializedSuperclassCount.decrementAndGet();
|
||||||
|
if (count == 0) {
|
||||||
|
checkAbstractMembers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
uninitializedCount++;
|
||||||
|
isAllInitialized = false;
|
||||||
|
}
|
||||||
|
sc = sc.superclass;
|
||||||
|
}
|
||||||
|
uninitializedSuperclassCount.set(uninitializedCount);
|
||||||
|
if (isAllInitialized) {
|
||||||
|
checkAbstractMembers();
|
||||||
|
}
|
||||||
|
lock:
|
||||||
|
synchronized (finalizersLock) {
|
||||||
|
if (__finalizers == null) break lock;
|
||||||
|
for (var finalizer : __finalizers) {
|
||||||
|
finalizer.run();
|
||||||
|
}
|
||||||
|
this.__finalizers = null;
|
||||||
|
}
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,13 +858,27 @@ public final class VmClass extends VmValue {
|
|||||||
for (var property : EconomicMaps.getValues(declaredProperties)) {
|
for (var property : EconomicMaps.getValues(declaredProperties)) {
|
||||||
if (property.isLocal()) continue;
|
if (property.isLocal()) continue;
|
||||||
|
|
||||||
// A property is considered a class property definition
|
// A property is considered a class property definition if any of the following are true:
|
||||||
// if it has a type annotation or has no superdefinition (ad-hoc case).
|
//
|
||||||
|
// 1. It has a type annotation.
|
||||||
|
// 2. It has no superdefinition.
|
||||||
|
// 3. It is not abstract but its superclass is.
|
||||||
|
//
|
||||||
// Otherwise, it is considered an object property definition,
|
// Otherwise, it is considered an object property definition,
|
||||||
// which means it affects the class prototype but not the class itself.
|
// which means it affects the class prototype but not the class itself.
|
||||||
// An example for the latter is when `Module.output` is overridden with `output { ... }`.
|
// An example for the latter is when `Module.output` is overridden with `output { ... }`.
|
||||||
if (property.getTypeNode() != null || !EconomicMaps.containsKey(result, property.getName())) {
|
if (property.getTypeNode() != null) {
|
||||||
EconomicMaps.put(result, property.getName(), property);
|
EconomicMaps.put(result, property.getName(), property);
|
||||||
|
} else {
|
||||||
|
var existingProperty = EconomicMaps.get(result, property.getName());
|
||||||
|
if (existingProperty != null && existingProperty.isAbstract() && !this.isAbstract()) {
|
||||||
|
property.lateInitTypeNodeAndModifiers(
|
||||||
|
existingProperty.getTypeNode(),
|
||||||
|
existingProperty.getModifiers() & ~VmModifier.ABSTRACT);
|
||||||
|
EconomicMaps.put(result, property.getName(), property);
|
||||||
|
} else if (existingProperty == null) {
|
||||||
|
EconomicMaps.put(result, property.getName(), property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1176,3 +1176,13 @@ Cannot follow redirect from ''https:'' URL to ''http:'' URL.\
|
|||||||
\n\
|
\n\
|
||||||
HTTP Request: `GET {0}`\n\
|
HTTP Request: `GET {0}`\n\
|
||||||
Redirected to: `{1}`
|
Redirected to: `{1}`
|
||||||
|
|
||||||
|
noImplementationForAbstractProperty=\
|
||||||
|
Class `{0}` should either be declared `abstract`, or should implement property `{1}`.
|
||||||
|
|
||||||
|
noImplementationForAbstractMethod=\
|
||||||
|
Class `{0}` should either be declared `abstract`, or should implement method `{1}`.
|
||||||
|
|
||||||
|
noImplementationForAbstractMembers=\
|
||||||
|
Class `{0}` should either be declared `abstract`, or implement the following members:\n\
|
||||||
|
{1}
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
abstract module Foo
|
||||||
|
|
||||||
|
abstract bar: Int
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
abstract class AbstractProperty {
|
||||||
|
abstract foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractMethod {
|
||||||
|
abstract function foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractPropertiesAndMethods {
|
||||||
|
abstract foo: Int
|
||||||
|
abstract bar: Int
|
||||||
|
abstract function foo(): Int
|
||||||
|
abstract function bar(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyClass extends AbstractProperty {
|
||||||
|
}
|
||||||
|
|
||||||
|
foo: MyClass
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
abstract class AbstractMethod {
|
||||||
|
abstract function foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyClass extends AbstractMethod {
|
||||||
|
}
|
||||||
|
|
||||||
|
foo: MyClass
|
||||||
|
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
abstract class AbstractPropertiesAndMethods {
|
||||||
|
abstract foo: Int
|
||||||
|
abstract bar: Int
|
||||||
|
abstract function foo(): Int
|
||||||
|
abstract function bar(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyClass extends AbstractPropertiesAndMethods {}
|
||||||
|
|
||||||
|
foo: MyClass
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
extends "../../input-helper/classes/AbstractModule.pkl"
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Class `abstractMemberNotImplemented1#MyClass` should either be declared `abstract`, or should implement property `foo`.
|
||||||
|
|
||||||
|
xx | class MyClass extends AbstractProperty {
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at abstractMemberNotImplemented1#MyClass (file:///$snippetsDir/input/errors/abstractMemberNotImplemented1.pkl)
|
||||||
|
|
||||||
|
xx | foo: MyClass
|
||||||
|
^^^^^^^
|
||||||
|
at abstractMemberNotImplemented1 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented1.pkl)
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Class `abstractMemberNotImplemented2#MyClass` should either be declared `abstract`, or should implement method `foo`.
|
||||||
|
|
||||||
|
x | class MyClass extends AbstractMethod {
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at abstractMemberNotImplemented2#MyClass (file:///$snippetsDir/input/errors/abstractMemberNotImplemented2.pkl)
|
||||||
|
|
||||||
|
x | foo: MyClass
|
||||||
|
^^^^^^^
|
||||||
|
at abstractMemberNotImplemented2 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented2.pkl)
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Class `abstractMemberNotImplemented3#MyClass` should either be declared `abstract`, or implement the following members:
|
||||||
|
* property `foo`
|
||||||
|
* property `bar`
|
||||||
|
* method `foo`
|
||||||
|
* method `bar`
|
||||||
|
|
||||||
|
x | class MyClass extends AbstractPropertiesAndMethods {}
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at abstractMemberNotImplemented3#MyClass (file:///$snippetsDir/input/errors/abstractMemberNotImplemented3.pkl)
|
||||||
|
|
||||||
|
xx | foo: MyClass
|
||||||
|
^^^^^^^
|
||||||
|
at abstractMemberNotImplemented3 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented3.pkl)
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Class `abstractMemberNotImplemented4` should either be declared `abstract`, or should implement property `bar`.
|
||||||
|
|
||||||
|
x | extends "../../input-helper/classes/AbstractModule.pkl"
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at abstractMemberNotImplemented4 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented4.pkl)
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
–– Pkl Error ––
|
||||||
|
Class `abstractMethodsNotImplemented#MyClass` either needs to be abstract, or should implement property `foo`.
|
||||||
|
|
||||||
|
x | class MyClass extends MyAbstractClass {
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
at abstractMethodsNotImplemented#MyClass (file:///$snippetsDir/input/errors/abstractMethodsNotImplemented.pkl)
|
||||||
|
|
||||||
|
x | foo: MyClass
|
||||||
|
^^^^^^^
|
||||||
|
at abstractMethodsNotImplemented (file:///$snippetsDir/input/errors/abstractMethodsNotImplemented.pkl)
|
||||||
@@ -3562,6 +3562,18 @@ external class Set<out Element> extends Collection<Element> {
|
|||||||
|
|
||||||
/// The difference of this set and [other].
|
/// The difference of this set and [other].
|
||||||
external function difference(other: Set): Set<Element>
|
external function difference(other: Set): Set<Element>
|
||||||
|
|
||||||
|
function indexOf(_): Int = throw("Set does not implement `indexOf`")
|
||||||
|
function indexOfOrNull(_): Int? = throw("Set does not implement `indexOfOrNull`")
|
||||||
|
|
||||||
|
function lastIndexOf(_): Int = throw("Set does not implement `lastIndexOf`")
|
||||||
|
function lastIndexOfOrNull(_): Int? = throw("Set does not implement `lastIndexOfOrNull`")
|
||||||
|
|
||||||
|
function findIndex(_): Int = throw("Set does not implement `findIndex`")
|
||||||
|
function findIndexOrNull(_): Int? = throw("Set does not implement `findIndexOrNull`")
|
||||||
|
|
||||||
|
function findLastIndex(_): Int = throw("Set does not implement `findLastIndex`")
|
||||||
|
function findLastIndexOrNull(_): Int? = throw("Set does not implement `findLastIndexOrNull`")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a map containing the given alternating [keysAndValues].
|
/// Creates a map containing the given alternating [keysAndValues].
|
||||||
|
|||||||
Reference in New Issue
Block a user