Skip to content

Commit

Permalink
Use shared TSCRuntime in JavaScriptParser
Browse files Browse the repository at this point in the history
Constructing a new `TSCRuntime` (and thereby a new `V8Runtime`) is a very costly operation, which is especially bad for unit tests, where this happens for every test case.

This commit changes that by using a shared `TSCRuntime` instance for all `JavaScriptParser` instances.

Additionally, this commit gets rid of the `javaScript()` methods in `ParserTest`. Instead, all tests now directly use the corresponding `Assertions` methods.
  • Loading branch information
knutwannheden committed Nov 15, 2023
1 parent e8375f0 commit d6ce060
Show file tree
Hide file tree
Showing 52 changed files with 329 additions and 282 deletions.
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

0 comments on commit d6ce060

Please sign in to comment.