Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/main/java/groovy/concurrent/Awaitable.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package groovy.concurrent;

import org.apache.groovy.lang.annotation.GroovyABI;
import org.apache.groovy.runtime.async.AsyncSupport;
import org.apache.groovy.runtime.async.GroovyPromise;

Expand Down Expand Up @@ -416,6 +417,7 @@ static <T> T withScope(Function<AsyncScope, T> body) {
* @param sources the awaitables, futures, or adapted objects to wait for
* @return an awaitable that resolves to a list of results
*/
@GroovyABI(since="6.0.0")
static Awaitable<List<Object>> all(Object... sources) {
return AsyncSupport.allAsync(sources);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/groovy/lang/Closure.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.apache.groovy.internal.util.UncheckedThrow;
import org.apache.groovy.io.StringBuilderWriter;
import org.apache.groovy.lang.annotation.GroovyABI;
import org.apache.groovy.util.Maps;
import org.codehaus.groovy.reflection.ReflectionCache;
import org.codehaus.groovy.reflection.stdclasses.CachedClosureClass;
Expand Down Expand Up @@ -345,6 +346,7 @@ public int getResolveStrategy() {
*
* @return the lexical {@code this} object
*/
@GroovyABI(since = "1.0")
public Object getThisObject() {
return thisObject;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/groovy/lang/Reference.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package groovy.lang;

import org.apache.groovy.lang.annotation.GroovyABI;
import org.codehaus.groovy.runtime.InvokerHelper;

import java.io.Serial;
Expand Down Expand Up @@ -96,6 +97,7 @@ public Object invokeMethod(String name, Object args) {
*
* @return the referenced value
*/
@GroovyABI(since = "1.0")
public T get() {
return value;
}
Expand All @@ -105,6 +107,7 @@ public T get() {
*
* @param value the new value
*/
@GroovyABI(since = "1.0")
public void set(T value) {
this.value = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.groovy.ast.tools;

import org.apache.groovy.lang.annotation.GroovyABI;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.Parameter;
Expand Down Expand Up @@ -97,6 +98,7 @@ public static ConstructorCallExpression getFirstIfSpecialConstructorCall(final S
* @param props the properties accepted by the constructor
* @return a statement that performs the validation
*/
@GroovyABI(since="4.0.0")
public static Statement checkPropNamesS(final VariableExpression namedArgs, final boolean pojo, final List<PropertyNode> props) {
if (!pojo) {
return stmt(callX(IMMUTABLE_TYPE, "checkPropNames", args(varX("this"), namedArgs)));
Expand Down
61 changes: 61 additions & 0 deletions src/main/java/org/apache/groovy/lang/annotation/GroovyABI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.groovy.lang.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;

/**
* Marks a program element as part of Groovy's binary ABI.
* <p>
* Elements annotated with {@code @GroovyABI} may be referenced directly by
* bytecode generated by the Groovy compiler. Their signatures and semantics
* therefore form part of the compatibility contract between compiled Groovy
* classes and the Groovy runtime.
* <p>
* Changes to annotated elements must preserve binary compatibility with
* previously compiled Groovy code unless the corresponding compatibility
* guarantee is intentionally dropped.
* <p>
* This annotation is intended only for members that are referenced by
* bytecode which may outlive the compiler version that produced it. Members
* used exclusively by bytecode generated at runtime by the current Groovy
* version should not be annotated.
* <p>
* Notes:
* <li>groovy.*: These classes are not annotated, as they are part of public API
* <li>runtime packages: These classes are normally internal and subject
* to removal or change. These classes must use this annotation if any of their methods
* is referenced by the compiler.
* <li>AST helper classes not in runtime: Some AST transforms leverage helper classes, that
* are not in a runtime package. These classes must be annotated.
*/
@Documented
@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
@Retention(SOURCE)
public @interface GroovyABI {
String since() default "";
}
13 changes: 13 additions & 0 deletions src/main/java/org/apache/groovy/runtime/async/AsyncSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import groovy.concurrent.AwaitResult;
import groovy.concurrent.Awaitable;
import groovy.concurrent.AwaitableAdapterRegistry;
import org.apache.groovy.lang.annotation.GroovyABI;

import java.io.Closeable;
import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -120,6 +121,7 @@ public static void resetExecutor() {
* Blocks the calling thread until the computation completes.
* The original exception is rethrown transparently.
*/
@GroovyABI(since="6.0.0")
public static <T> T await(Awaitable<T> awaitable) {
try {
return awaitable.get();
Expand All @@ -131,6 +133,7 @@ public static <T> T await(Awaitable<T> awaitable) {
}

/** Awaits a {@link CompletableFuture} using non-interruptible {@code join()}. */
@GroovyABI(since="6.0.0")
public static <T> T await(CompletableFuture<T> future) {
try {
return future.join();
Expand All @@ -142,11 +145,13 @@ public static <T> T await(CompletableFuture<T> future) {
}

/** Awaits a {@link CompletionStage} by converting to CompletableFuture. */
@GroovyABI(since="6.0.0")
public static <T> T await(CompletionStage<T> stage) {
return await(stage.toCompletableFuture());
}

/** Awaits a {@link Future}. Delegates to the CF overload if applicable. */
@GroovyABI(since="6.0.0")
public static <T> T await(Future<T> future) {
if (future instanceof CompletableFuture<T> cf) {
return await(cf);
Expand All @@ -171,6 +176,7 @@ public static <T> T await(Future<T> future) {
* {@code CompletionStage}, and {@code Future} when a value implements more
* than one of them (e.g. {@link CompletableFuture}).
*/
@GroovyABI(since="6.0.0")
@SuppressWarnings("unchecked")
public static <T> T await(Object source) {
if (source == null) return null;
Expand Down Expand Up @@ -202,6 +208,7 @@ public static <T> Awaitable<T> executeAsync(Supplier<T> supplier, Executor execu
/**
* Executes the given supplier asynchronously using the default executor.
*/
@GroovyABI(since="6.0.0")
public static <T> Awaitable<T> async(Supplier<T> supplier) {
return executeAsync(supplier, AsyncExecutors.getExecutor());
}
Expand All @@ -221,6 +228,7 @@ public static <T> Awaitable<T> go(Supplier<T> supplier) {
* Called by compiler-generated code at the start of closures
* containing {@code defer} statements.
*/
@GroovyABI(since="6.0.0")
public static Deque<Callable<?>> createDeferScope() {
return new ArrayDeque<>();
}
Expand All @@ -229,6 +237,7 @@ public static Deque<Callable<?>> createDeferScope() {
* Registers a deferred action in the given scope. Actions execute in LIFO
* order when {@link #executeDeferScope} is called (in the finally block).
*/
@GroovyABI(since="6.0.0")
public static void defer(Deque<Callable<?>> scope,
Callable<?> action) {
if (scope == null) {
Expand Down Expand Up @@ -287,6 +296,7 @@ private static void awaitDeferredResult(Object result) {
* @param bridge the GeneratorBridge instance (injected as synthetic parameter)
* @param value the value to yield
*/
@GroovyABI(since="6.0.0")
public static void yieldReturn(Object bridge, Object value) {
if (!(bridge instanceof GeneratorBridge<?>)) {
throw new IllegalStateException("yield return can only be used inside an async generator");
Expand All @@ -307,6 +317,7 @@ public static void yieldReturn(Object bridge, Object value) {
* @param <T> the element type
* @return an Iterable that yields values from the generator
*/
@GroovyABI(since="6.0.0")
public static <T> Iterable<T> asyncGenerator(Consumer<Object> body) {
Objects.requireNonNull(body, "body must not be null");
GeneratorBridge<T> bridge = new GeneratorBridge<>();
Expand Down Expand Up @@ -335,6 +346,7 @@ public static <T> Iterable<T> asyncGenerator(Consumer<Object> body) {
* @param <T> the element type
* @return an iterable
*/
@GroovyABI(since="6.0.0")
@SuppressWarnings("unchecked")
public static <T> Iterable<T> toIterable(Object source) {
if (source == null) return Collections.emptyList();
Expand All @@ -354,6 +366,7 @@ public static <T> Iterable<T> toIterable(Object source) {
* cannot mask the original loop error; prefer robust {@code close()}
* implementations.
*/
@GroovyABI(since="6.0.0")
public static void closeIterable(Object source) {
if (source instanceof Closeable c) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import groovy.lang.Closure;
import groovy.lang.GroovyRuntimeException;
import org.apache.groovy.lang.annotation.GroovyABI;
import org.apache.groovy.lang.annotation.Incubating;
import org.codehaus.groovy.runtime.InvokerHelper;

Expand Down Expand Up @@ -52,6 +53,7 @@ private NestedCopyWithSupport() {
* (nested-aware) {@code copyWith(Map)}. The block is thus pure sugar over
* the map form, inheriting its closed-type-domain and identity guarantees.
*/
@GroovyABI(since="6.0.0")
public static Object applyBlock(final Object self, final Closure<?> block) {
Map<Object, Object> sink = new LinkedHashMap<>();
Closure<?> c = (Closure<?>) block.clone();
Expand All @@ -63,6 +65,7 @@ public static Object applyBlock(final Object self, final Closure<?> block) {
}

@SuppressWarnings("unchecked")
@GroovyABI(since="6.0.0")
public static Map<Object, Object> flatten(final Object self, final Map<Object, Object> raw) {
if (raw == null) return new LinkedHashMap<>();
Map<Object, Object> flat = new LinkedHashMap<>();
Expand Down
Loading
Loading