Skip to content

Commit

Permalink
Stateless parser API
Browse files Browse the repository at this point in the history
Stateless (static) parser interface. Buffer-reuse optimization is now hidden
behind JNI FFI implementation. Fixes #11121 and prevents similar bugs.
  • Loading branch information
kazcw committed Sep 20, 2024
1 parent c996707 commit ebd4caf
Show file tree
Hide file tree
Showing 16 changed files with 94 additions and 273 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import org.enso.compiler.core.EnsoParser;

public final class EnsoLibraryFeature implements Feature {
@Override
Expand Down Expand Up @@ -45,14 +46,14 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
*/

var classes = new TreeSet<String>();
try (var parser = new org.enso.compiler.core.EnsoParser()) {
try {
for (var p : libs) {
var result = PackageManager$.MODULE$.Default().loadPackage(p.toFile());
if (result.isSuccess()) {
var pkg = result.get();
for (var src : pkg.listSourcesJava()) {
var code = Files.readString(src.file().toPath());
var ir = parser.compile(code);
var ir = EnsoParser.compile(code);
for (var imp : asJava(ir.imports())) {
if (imp instanceof Polyglot poly && poly.entity() instanceof Polyglot.Java entity) {
var name = new StringBuilder(entity.getJavaName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import org.enso.compiler.phase.exports.{
ExportsResolution
}
import org.enso.syntax2.Tree
import org.enso.syntax2.Parser

import java.io.PrintStream
import java.util.concurrent.{
Expand Down Expand Up @@ -69,7 +70,6 @@ class Compiler(
if (config.outputRedirect.isDefined)
new PrintStream(config.outputRedirect.get)
else context.getOut
private lazy val ensoCompiler: EnsoParser = new EnsoParser()

/** Java accessor */
def getConfig(): CompilerConfig = config
Expand Down Expand Up @@ -598,11 +598,8 @@ class Compiler(
)

val src = context.getCharacters(module)
val idMap = context.getIdMap(module)
val tree = ensoCompiler.parse(src)
val expr =
if (idMap == null) ensoCompiler.generateIR(tree)
else ensoCompiler.generateModuleIr(tree, idMap.values)
val idMap = Option(context.getIdMap(module))
val expr = EnsoParser.compile(src, idMap.map(_.values).orNull)

val exprWithModuleExports =
if (context.isSynthetic(module))
Expand Down Expand Up @@ -685,9 +682,8 @@ class Compiler(
inlineContext: InlineContext
): Option[(InlineContext, Expression)] = {
val newContext = inlineContext.copy(freshNameSupply = Some(freshNameSupply))
val tree = ensoCompiler.parse(srcString)

ensoCompiler.generateIRInline(tree).map { ir =>
EnsoParser.compileInline(srcString).map { ir =>
val compilerOutput = runCompilerPhasesInline(ir, newContext)
runErrorHandlingInline(compilerOutput, newContext)
(newContext, compilerOutput)
Expand All @@ -700,7 +696,7 @@ class Compiler(
* @return A Tree representation of `source`
*/
def parseInline(source: CharSequence): Tree =
ensoCompiler.parse(source)
Parser.parse(source)

/** Enhances the provided IR with import/export statements for the provided list
* of fully qualified names of modules. The statements are considered to be "synthetic" i.e. compiler-generated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import org.enso.compiler.data.CompilerConfig
import org.enso.common.CompilationStage
import org.enso.compiler.phase.exports.ExportsResolution

import scala.util.Using

/** A phase responsible for initializing the builtins' IR from the provided
* source.
*/
Expand Down Expand Up @@ -44,9 +42,7 @@ object BuiltinsIrBuilder {
freshNameSupply = Some(freshNameSupply),
compilerConfig = CompilerConfig(warningsEnabled = false)
)
val initialIr = Using(new EnsoParser) { compiler =>
compiler.compile(module.getCharacters)
}.get
val initialIr = EnsoParser.compile(module.getCharacters)
val irAfterModDiscovery = passManager.runPassesOnModule(
initialIr,
moduleContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import org.enso.text.editing.{IndexedSource, TextEditor}

import java.util.UUID
import scala.collection.mutable
import scala.util.Using

/** The changeset of a module containing the computed list of invalidated
* expressions.
Expand Down Expand Up @@ -97,14 +96,12 @@ final class ChangesetBuilder[A: TextEditor: IndexedSource](
}

val source = Source.newBuilder("enso", value, null).build
Using(new EnsoParser) { compiler =>
compiler
.generateIRInline(compiler.parse(source.getCharacters()))
.flatMap(_ match {
case ir: Literal => Some(ir.setLocation(oldIr.location))
case _ => None
})
}.get
EnsoParser
.compileInline(source.getCharacters())
.flatMap(_ match {
case ir: Literal => Some(ir.setLocation(oldIr.location))
case _ => None
})
}

oldIr match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,6 @@ trait CompilerTestSetup {
*/
implicit private class ToIrModule(source: String) {

/** Converts program text to a top-level Enso module.
*
* @return the [[IR]] representing [[source]]
*/
def toIrModule: Module = {
val compiler = new EnsoParser()
try compiler.compile(source)
finally compiler.close()
}
}

/** An extension method to allow converting string source code to IR as an
* expression.
*
* @param source the source code to convert
*/
implicit private class ToIrExpression(source: String) {

/** Converts the program text to an Enso expression.
*
* @return the [[IR]] representing [[source]], if it is a valid expression
*/
def toIrExpression: Option[Expression] = {
val compiler = new EnsoParser()
try compiler.generateIRInline(compiler.parse(source))
finally compiler.close()
}
}

/** Provides an extension method allowing the running of a specified list of
* passes on the provided IR.
*
Expand Down Expand Up @@ -112,7 +83,7 @@ trait CompilerTestSetup {
* @return IR appropriate for testing the alias analysis pass as a module
*/
def preprocessModule(implicit moduleContext: ModuleContext): Module = {
source.toIrModule.runPasses(passManager, moduleContext)
EnsoParser.compile(source).runPasses(passManager, moduleContext)
}

/** Translates the source code into appropriate IR for testing this pass
Expand All @@ -123,7 +94,7 @@ trait CompilerTestSetup {
def preprocessExpression(implicit
inlineContext: InlineContext
): Option[Expression] = {
source.toIrExpression.map(_.runPasses(passManager, inlineContext))
EnsoParser.compileInline(source).map(_.runPasses(passManager, inlineContext))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,10 @@
import org.enso.compiler.core.EnsoParser;
import org.enso.compiler.core.IR;
import org.enso.compiler.core.ir.Module;
import org.junit.AfterClass;
import org.junit.BeforeClass;

public abstract class CompilerTests {

protected static EnsoParser ensoCompiler;

@BeforeClass
public static void initEnsoParser() {
ensoCompiler = new EnsoParser();
}

@AfterClass
public static void closeEnsoParser() throws Exception {
ensoCompiler.close();
}

protected static Module parse(CharSequence code) {
Module ir = ensoCompiler.compile(code);
Module ir = EnsoParser.compile(code);
assertNotNull("IR was generated", ir);
return ir;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,6 @@
import scala.Function1;

public class VectorArraySignatureTest {
private static EnsoParser ensoCompiler;

@BeforeClass
public static void initEnsoParser() {
ensoCompiler = new EnsoParser();
}

@AfterClass
public static void closeEnsoParser() throws Exception {
ensoCompiler.close();
ensoCompiler = null;
}

@Test
public void testParseVectorAndArray() throws Exception {
var p = Paths.get("../../distribution/").toFile().getCanonicalFile();
Expand Down Expand Up @@ -81,7 +68,7 @@ public FileVisitResult postVisitDirectory(Path t, IOException ioe) throws IOExce
var vectorSrc = Files.readString(vectorAndArray[1]);

var arrayIR =
ensoCompiler
EnsoParser
.compile(arraySrc)
.preorder()
.filter(
Expand All @@ -95,7 +82,7 @@ public FileVisitResult postVisitDirectory(Path t, IOException ioe) throws IOExce
})
.head();
var vectorIR =
ensoCompiler
EnsoParser
.compile(vectorSrc)
.preorder()
.filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ trait CompilerRunner {
* @return the [[IR]] representing [[source]]
*/
def toIrModule: Module = {
val compiler = new EnsoParser()
try compiler.compile(source)
finally compiler.close()
EnsoParser.compile(source)
}
}

Expand All @@ -61,9 +59,7 @@ trait CompilerRunner {
* @return the [[IR]] representing [[source]], if it is a valid expression
*/
def toIrExpression: Option[Expression] = {
val compiler = new EnsoParser()
try compiler.generateIRInline(compiler.parse(source))
finally compiler.close()
EnsoParser.compileInline(source)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,23 @@
import org.enso.compiler.core.ir.Location;
import org.enso.compiler.core.ir.Module;
import org.enso.syntax2.Parser;
import org.enso.syntax2.Tree;

public final class EnsoParser implements AutoCloseable {
private final Parser parser;

public EnsoParser() {
Parser p;
try {
p = Parser.create();
} catch (LinkageError err) {
err.printStackTrace();
throw err;
}
this.parser = p;
public final class EnsoParser {
public static Module compile(CharSequence src) {
return compile(src, null);
}

@Override
public void close() throws Exception {
if (parser != null) {
parser.close();
public static Module compile(CharSequence src, Map<Location, UUID> idMap) {
var tree = Parser.parse(src);
var treeToIr = TreeToIr.MODULE;
if (idMap != null) {
treeToIr = new TreeToIr(idMap);
}
return treeToIr.translate(tree);
}

public Module compile(CharSequence src) {
var tree = parser.parse(src);
return generateIR(tree);
}

public Tree parse(CharSequence src) {
return parser.parse(src);
}

public Module generateIR(Tree t) {
return TreeToIr.MODULE.translate(t);
}

public Module generateModuleIr(Tree t, Map<Location, UUID> idMap) {
var treeToIr = new TreeToIr(idMap);
return treeToIr.translate(t);
}

public scala.Option<Expression> generateIRInline(Tree t) {
return TreeToIr.MODULE.translateInline(t);
public static scala.Option<Expression> compileInline(CharSequence src) {
var tree = Parser.parse(src);
return TreeToIr.MODULE.translateInline(tree);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,6 @@
import scala.jdk.javaapi.CollectionConverters;

public class EnsoParserTest {
private static EnsoParser ensoCompiler;

@BeforeClass
public static void initEnsoParser() {
try {
ensoCompiler = new EnsoParser();
} catch (LinkageError e) {
throw new AssertionError(e);
}
}

@AfterClass
public static void closeEnsoParser() throws Exception {
if (ensoCompiler != null) ensoCompiler.close();
}

@Test
public void testParseMain7Foo() {
parseTest("""
Expand Down Expand Up @@ -1529,7 +1513,9 @@ private static void equivalenceTest(String code1, String code2) throws IOExcepti
}

private static Module compile(String code) {
return compile(ensoCompiler, code);
var ir = EnsoParser.compile(code);
assertNotNull("IR was generated", ir);
return ir;
}

private void expectNoErrorsInIr(Module moduleIr) {
Expand All @@ -1544,12 +1530,6 @@ private void expectNoErrorsInIr(Module moduleIr) {
});
}

public static Module compile(EnsoParser c, String code) {
var ir = c.compile(code);
assertNotNull("IR was generated", ir);
return ir;
}

static void assertIR(String msg, Module old, Module now) throws IOException {
Function<IR, String> filter = f -> simplifyIR(f, true, true, false);
String ir1 = filter.apply(old);
Expand Down
Loading

0 comments on commit ebd4caf

Please sign in to comment.