Skip to content

Commit

Permalink
Implement Polyglot support as visitors (openrewrite#1055)
Browse files Browse the repository at this point in the history
* Implement Polyglot support as visitors

* minor cleanup

* Implement randomId helper

* Disable GraalVM-only test
  • Loading branch information
jbrisbin authored Sep 29, 2021
1 parent 91a16dd commit e0b046d
Show file tree
Hide file tree
Showing 19 changed files with 1,406 additions and 108 deletions.
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.workers.max=6
org.gradle.jvmargs=-Xmx3g
1 change: 1 addition & 0 deletions rewrite-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
api("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.+")

implementation("org.graalvm.sdk:graal-sdk:latest.release")
testImplementation("org.graalvm.sdk:graal-sdk:latest.release")
implementation("org.apache.commons:commons-io:latest.release")
implementation("org.apache.commons:commons-compress:latest.release")

Expand Down
23 changes: 23 additions & 0 deletions rewrite-core/src/main/java/org/openrewrite/PolyglotExport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2021 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite;

public @interface PolyglotExport {
String typeScript();

String llvm();
}
21 changes: 21 additions & 0 deletions rewrite-core/src/main/java/org/openrewrite/PolyglotNamespace.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2021 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite;

public @interface PolyglotNamespace {
String[] value();
}
1 change: 1 addition & 0 deletions rewrite-core/src/main/java/org/openrewrite/Recipe.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
* <p>
* returns a list of {@link Result results} for each modified {@link SourceFile}
*/
@PolyglotExport(typeScript = "Recipe", llvm = "Recipe")
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@c")
public abstract class Recipe {
public static final String PANIC = "__AHHH_PANIC!!!__";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public NpmRegistryModuleLoader(String registry, String... modules) {
String main = packageDescriptor.getMain();

Path jsPath = Paths.get(registry, module, main);
evalPolyglotRecipe(module, Source.newBuilder(JS, jsPath.toFile()).name(jsPath.toString()).build());
evalPolyglotRecipe(Source.newBuilder(JS, jsPath.toFile()).name(jsPath.toString()).build());
} else {
URI srcUri = URI.create(registry + module);
PackagesDescriptor packages = mapper.readValue(srcUri.toURL(), PackagesDescriptor.class);
Expand All @@ -75,7 +75,7 @@ public NpmRegistryModuleLoader(String registry, String... modules) {
//noinspection StatementWithEmptyBody
for (int i = 0; i < buff.length; i += tgzIn.read(buff, i, tgzIn.getRecordSize())) {
}
evalPolyglotRecipe(module, Source.newBuilder(JS, ByteSequence.create(buff), name).build());
evalPolyglotRecipe(Source.newBuilder(JS, ByteSequence.create(buff), name).build());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,25 @@
package org.openrewrite.config;

import lombok.RequiredArgsConstructor;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.polyglot.Polyglot;
import org.openrewrite.polyglot.PolyglotParser;
import org.openrewrite.polyglot.PolyglotRecipe;
import org.openrewrite.polyglot.PolyglotUtils;
import org.openrewrite.polyglot.PolyglotVisitor;
import org.openrewrite.style.NamedStyles;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.openrewrite.polyglot.PolyglotUtils.maybeInstantiateOrInvoke;
import static org.openrewrite.polyglot.PolyglotValueMappings.hasGetVisitor;

public class PolyglotResourceLoader implements ResourceLoader {

private static final ThreadLocal<Engine> ENGINES = new InheritableThreadLocal<Engine>() {
@Override
protected Engine initialValue() {
return Engine.newBuilder()
.allowExperimentalOptions(true)
.build();
}
};

private final List<PolyglotRecipes> recipes = new ArrayList<>();
private final List<NamedStyles> styles = new ArrayList<>();

Expand All @@ -58,9 +46,9 @@ protected Engine initialValue() {
public PolyglotResourceLoader(Source... sources) {
for (Source src : sources) {
try {
evalPolyglotRecipe(src.getName(), src);
evalPolyglotRecipe(src);
} catch (IOException e) {
throw new RuntimeException(e);
throw new RuntimeException(e.getMessage(), e);
}
}
}
Expand Down Expand Up @@ -92,44 +80,44 @@ public Collection<RecipeExample> listRecipeExamples() {
return recipeExamples;
}

public void evalPolyglotRecipe(String name, Source src) throws IOException {
String language = PolyglotUtils.getLanguage(src);

recipes.add(new PolyglotRecipes(ctx -> {
ctx.eval(src);
Value bindings = ctx.getBindings(language);
return bindings.getMemberKeys().stream()
.flatMap(exportName -> maybeInstantiateOrInvoke(bindings, exportName)
.map(exportVal -> exportVal.getMemberKeys().stream()
.map(recipeName -> maybeInstantiateOrInvoke(exportVal, recipeName)
.map(v -> new PolyglotRecipe(name + "/" + recipeName, v))))
.orElseGet(Stream::empty))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}));
public void evalPolyglotRecipe(Source moduleSrc) throws IOException {
recipes.add(new PolyglotRecipes(moduleSrc));
recipes.stream().flatMap(r -> r.getRecipes().stream())
.forEach(r -> {
recipeDescriptors.add(r.getRecipeDescriptor());
categoryDescriptors.addAll(r.getCategoryDescriptors());
styles.addAll(r.getNamedStyles());
});
}

@lombok.Value
@RequiredArgsConstructor
private static class PolyglotRecipes {
ThreadLocal<Context> context = new InheritableThreadLocal<Context>() {
@Override
protected Context initialValue() {
return Context.newBuilder()
.engine(ENGINES.get())
.allowAllAccess(true)
.allowExperimentalOptions(true)
.build();
}
};

Function<Context, List<PolyglotRecipe>> recipes;
PolyglotParser parser = new PolyglotParser();
Source source;

ThreadLocal<List<PolyglotRecipe>> perThreadRecipes = new InheritableThreadLocal<List<PolyglotRecipe>>() {
@Override
protected List<PolyglotRecipe> initialValue() {
return recipes.apply(context.get());
List<Polyglot.Source> sources = parser.parse(new InMemoryExecutionContext(), source);
List<PolyglotRecipe> recipes = new ArrayList<>();

PolyglotVisitor<List<PolyglotRecipe>> recipesVisitor = new PolyglotVisitor<List<PolyglotRecipe>>() {
@Override
public Polyglot visitInstantiable(Polyglot.Instantiable instantiable, List<PolyglotRecipe> l) {
Polyglot.Instance inst = instantiable.instantiate();
if (hasGetVisitor().test(inst.getValue())) {
inst.as(PolyglotRecipe.class).ifPresent(l::add);
return inst;
}
return instantiable;
}
};
for (Polyglot.Source src : sources) {
recipesVisitor.visitMembers(src.getMembers(), recipes);
}

return recipes;
}
};

Expand Down
19 changes: 15 additions & 4 deletions rewrite-core/src/main/java/org/openrewrite/internal/ListUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.openrewrite.internal;

import org.graalvm.polyglot.Value;
import org.openrewrite.internal.lang.Nullable;

import java.util.ArrayList;
Expand Down Expand Up @@ -110,6 +111,16 @@ public static <T> List<T> mapFirst(List<T> ls, UnaryOperator<T> mapFirst) {
return ls;
}

@SuppressWarnings({"rawtypes", "unchecked"})
public static <T> List<T> mapValues(Value values, BiFunction<Integer, Value, T> mapFn) {
final int size = ((Long) values.getArraySize()).intValue();
List ls = new ArrayList(size);
for (int i = 0; i < size; i++) {
ls.set(i, mapFn.apply(i, values.getArrayElement(i)));
}
return ls;
}

public static <T> List<T> map(List<T> ls, BiFunction<Integer, T, T> map) {
if (ls == null || ls.isEmpty()) {
return ls;
Expand Down Expand Up @@ -191,22 +202,22 @@ public static <T> List<T> flatMap(List<T> ls, Function<T, Object> flatMap) {
}

public static <T> List<T> concat(@Nullable List<T> ls, @Nullable T t) {
if(t == null && ls == null) {
if (t == null && ls == null) {
return emptyList();
}
List<T> newLs = ls == null ? new ArrayList<>(1) : new ArrayList<>(ls);
if(t != null) {
if (t != null) {
newLs.add(t);
}
return newLs;
}

public static <T> List<T> concat(@Nullable T t, @Nullable List<T> ls) {
if(t == null && ls == null) {
if (t == null && ls == null) {
return emptyList();
}
List<T> newLs = ls == null ? new ArrayList<>(1) : new ArrayList<>(ls.size() + 1);
if(t != null) {
if (t != null) {
newLs.add(t);
}
if (ls != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@PolyglotNamespace("OpenRewrite")
@NonNullApi
package org.openrewrite;

Expand Down
Loading

0 comments on commit e0b046d

Please sign in to comment.