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

Use shared TSCRuntime in JavaScriptParser #77

Merged
merged 1 commit into from
Nov 15, 2023
Merged
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 build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies {
implementation(platform("org.openrewrite:rewrite-bom:$latest"))
implementation("org.openrewrite:rewrite-java")

compileOnly("org.assertj:assertj-core:latest.release")
testImplementation("org.assertj:assertj-core:latest.release")
testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
Expand Down
69 changes: 60 additions & 9 deletions src/main/java/org/openrewrite/javascript/Assertions.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,88 @@


import org.intellij.lang.annotations.Language;
import org.openrewrite.ExecutionContext;
import org.openrewrite.ParseExceptionResult;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Space;
import org.openrewrite.javascript.tree.JS;
import org.openrewrite.test.SourceSpec;
import org.openrewrite.test.SourceSpecs;

import java.util.function.Consumer;

import static org.assertj.core.api.Assertions.assertThat;

public final class Assertions {

private Assertions() {
}

public static SourceSpecs javaScript(@Language("js") @Nullable String before) {
public static SourceSpecs javaScript(@Language("typescript") @Nullable String before) {
return javaScript(before, s -> {
});
}

public static SourceSpecs javaScript(@Language("js") @Nullable String before, Consumer<SourceSpec<JS.CompilationUnit>> spec) {
SourceSpec<JS.CompilationUnit> js = new SourceSpec<>(JS.CompilationUnit.class, null, JavaScriptParser.builder(), before, null);
spec.accept(js);
return js;
public static SourceSpecs javaScript(@Language("typescript") @Nullable String before, Consumer<SourceSpec<JS.CompilationUnit>> spec) {
return javaScript0(before, null, spec);
}

public static SourceSpecs javaScript(@Language("js") @Nullable String before, @Language("js") String after) {
public static SourceSpecs javaScript(@Language("typescript") @Nullable String before, @Language("typescript") String after) {
return javaScript(before, after, s -> {
});
}

public static SourceSpecs javaScript(@Language("js") @Nullable String before, @Language("js") String after,
public static SourceSpecs javaScript(@Language("typescript") @Nullable String before, @Language("typescript") String after,
Consumer<SourceSpec<JS.CompilationUnit>> spec) {
SourceSpec<JS.CompilationUnit> js = new SourceSpec<>(JS.CompilationUnit.class, null, JavaScriptParser.builder(), before, s -> after);
spec.accept(js);
return javaScript0(before, after, spec);
}

private static SourceSpec<JS.CompilationUnit> javaScript0(@Language("typescript") @Nullable String before, @Language("typescript") @Nullable String after, Consumer<SourceSpec<JS.CompilationUnit>> spec) {
SourceSpec<JS.CompilationUnit> js = new SourceSpec<>(
JS.CompilationUnit.class,
null,
JavaScriptParser.builder(),
before,
SourceSpec.ValidateSource.noop,
Assertions::customizeExecutionContext
);
if (after != null) {
js = js.after(s -> after);
}
acceptSpec(spec, js);
return js;
}

static void customizeExecutionContext(ExecutionContext ctx) {
}

private static void acceptSpec(Consumer<SourceSpec<JS.CompilationUnit>> spec, SourceSpec<JS.CompilationUnit> javaScript) {
Consumer<JS.CompilationUnit> userSuppliedAfterRecipe = javaScript.getAfterRecipe();
javaScript.afterRecipe(userSuppliedAfterRecipe::accept);
isFullyParsed().andThen(spec).accept(javaScript);
}

public static Consumer<SourceSpec<JS.CompilationUnit>> isFullyParsed() {
return spec -> spec.afterRecipe(cu -> {
new JavaScriptIsoVisitor<Integer>() {
@Override
public Space visitSpace(Space space, Space.Location loc, Integer integer) {
assertThat(space.getWhitespace().trim()).isEmpty();
return super.visitSpace(space, loc, integer);
}
}.visit(cu, 0);

new JavaScriptVisitor<Integer>() {
@Override
public @Nullable J preVisit(J tree, Integer integer) {
if (tree instanceof J.Unknown) {
((J.Unknown) tree).getSource().getMarkers().findFirst(ParseExceptionResult.class)
.ifPresent(result -> assertThat(result.getMessage()).isEqualTo(""));
}
return super.preVisit(tree, integer);
}
}.visit(cu, 0);
});
}
}
91 changes: 81 additions & 10 deletions src/main/java/org/openrewrite/javascript/JavaScriptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@
*/
package org.openrewrite.javascript;

import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Parser;
import org.openrewrite.SourceFile;
import org.openrewrite.internal.lang.NonNull;
import org.openrewrite.internal.EncodingDetectingInputStream;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.javascript.internal.TSCMapper;
import org.openrewrite.java.internal.JavaTypeCache;
import org.openrewrite.javascript.internal.JavetNativeBridge;
import org.openrewrite.javascript.internal.TypeScriptParserVisitor;
import org.openrewrite.javascript.internal.tsc.TSCRuntime;
import org.openrewrite.javascript.tree.JS;
import org.openrewrite.style.NamedStyles;
import org.openrewrite.tree.ParseError;
import org.openrewrite.tree.ParsingEventListener;
import org.openrewrite.tree.ParsingExecutionContextView;

import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -35,14 +42,36 @@

public class JavaScriptParser implements Parser {

private static final TSCRuntime RUNTIME;

static {
JavetNativeBridge.init();
RUNTIME = TSCRuntime.init();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
RUNTIME.close();
}
});
}

@Value
private static class SourceWrapper {
Parser.Input input;
Path sourcePath;
Charset charset;
boolean isCharsetBomMarked;
String sourceText;
}

private final Collection<NamedStyles> styles;

private JavaScriptParser(Collection<NamedStyles> styles) {
this.styles = styles;
}

@Override
public Stream<SourceFile> parse(@NonNull String... sources) {
public Stream<SourceFile> parse(String... sources) {
List<Input> inputs = new ArrayList<>(sources.length);
for (int i = 0; i < sources.length; i++) {
Path path = Paths.get("f" + i + ".js");
Expand All @@ -64,14 +93,56 @@ public Stream<SourceFile> parse(@NonNull String... sources) {
@Override
public Stream<SourceFile> parseInputs(Iterable<Input> sources, @Nullable Path relativeTo, ExecutionContext ctx) {
ParsingExecutionContextView pctx = ParsingExecutionContextView.view(ctx);
List<SourceFile> outputs;
try (TSCMapper mapper = new TSCMapper(relativeTo, styles, pctx) {}) {
for (Input source : sources) {
mapper.add(source);
}
outputs = mapper.build();
Map<Path, SourceWrapper> sourcesByRelativePath = new LinkedHashMap<>();

for (Input input : sources) {
EncodingDetectingInputStream is = input.getSource(pctx);
String inputSourceText = is.readFully();
Path relativePath = input.getRelativePath(relativeTo);

SourceWrapper source = new SourceWrapper(
input,
relativePath,
is.getCharset(),
is.isCharsetBomMarked(),
inputSourceText
);
sourcesByRelativePath.put(relativePath, source);
}
return outputs.stream();

List<SourceFile> compilationUnits = new ArrayList<>(sourcesByRelativePath.size());
ParsingEventListener parsingListener = ParsingExecutionContextView.view(pctx).getParsingListener();
Map<Path, String> sourceTextsForTSC = new LinkedHashMap<>();
sourcesByRelativePath.forEach((relativePath, sourceText) -> {
sourceTextsForTSC.put(relativePath, sourceText.sourceText);
});

RUNTIME.parseSourceTexts(
sourceTextsForTSC,
(node, context) -> {
SourceWrapper source = sourcesByRelativePath.get(context.getRelativeSourcePath());
parsingListener.startedParsing(source.getInput());
TypeScriptParserVisitor fileMapper = new TypeScriptParserVisitor(
node,
context,
source.getSourcePath(),
new JavaTypeCache(),
source.getCharset().toString(),
source.isCharsetBomMarked(),
styles
);
SourceFile cu;
try {
cu = fileMapper.visitSourceFile();
parsingListener.parsed(source.getInput(), cu);
} catch (Throwable t) {
((ExecutionContext) pctx).getOnError().accept(t);
cu = ParseError.build(JavaScriptParser.builder().build(), source.getInput(), relativeTo, pctx, t);
}
compilationUnits.add(cu);
}
);
return compilationUnits.stream();
}

private final static List<String> EXTENSIONS = Collections.unmodifiableList(Arrays.asList(
Expand Down
121 changes: 0 additions & 121 deletions src/main/java/org/openrewrite/javascript/internal/TSCMapper.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@
import com.caoccao.javet.values.primitive.V8ValuePrimitive;
import com.caoccao.javet.values.reference.V8ValueArray;
import com.caoccao.javet.values.reference.V8ValueFunction;
import com.caoccao.javet.values.reference.V8ValueMap;
import com.caoccao.javet.values.reference.V8ValueObject;
import org.intellij.lang.annotations.Language;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map;

public class TSCInstanceOfChecks extends TSCV8ValueHolder {

Expand Down Expand Up @@ -77,7 +74,7 @@ public static TSCInstanceOfChecks fromJS(V8ValueObject tsGlobalsV8) {
}
}

@Language("javascript")
@Language("typescript")
String code = "" +
"(arg) => {\n" +
" for (let i = 0; i < ctors.length; i++) {\n" +
Expand Down
Loading
Loading