Polyglot Import Renaming (#1179)

This commit is contained in:
Marcin Kostrzewa 2020-10-01 16:27:40 +02:00 committed by GitHub
parent 3d88e7f227
commit 3eee990429
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 22 deletions

View File

@ -97,10 +97,12 @@ object AstToIr {
val imports = presentBlocks.collect {
case AST.Import.any(list) => translateImport(list)
case AST.JavaImport.any(imp) =>
val pkg = imp.path.init.map(_.name)
val cls = imp.path.last.name
val pkg = imp.path.init.map(_.name)
val cls = imp.path.last.name
val rename = imp.rename.map(_.name)
Module.Scope.Import.Polyglot(
Module.Scope.Import.Polyglot.Java(pkg.mkString("."), cls),
rename,
getIdentifiedLocation(imp)
)
}

View File

@ -157,11 +157,10 @@ class IrToTruffle(
// Register the imports in scope
imports.foreach {
case Import.Polyglot(Import.Polyglot.Java(pkg, cls), _, _, _) =>
val fullName = s"$pkg.$cls"
case poly @ Import.Polyglot(i: Import.Polyglot.Java, _, _, _, _) =>
this.moduleScope.registerPolyglotSymbol(
cls,
context.getEnvironment.lookupHostSymbol(fullName)
poly.getVisibleName,
context.getEnvironment.lookupHostSymbol(i.getJavaName)
)
case i: Import.Module =>
this.moduleScope.addImport(

View File

@ -718,6 +718,12 @@ object IR {
sealed trait Entity {
val langName: String
/** Returns the name this object is visible as from Enso code.
*
* @return the visible name of this object
*/
def getVisibleName: String
def showCode(indent: Int = 0): String
}
@ -731,6 +737,14 @@ object IR {
extends Entity {
val langName = "java"
override def getVisibleName: String = className
/** Returns the fully qualified Java name of this object.
*
* @return the Java-side name of the imported entity
*/
def getJavaName: String = s"$packageName.$className"
override def showCode(indent: Int): String =
s"$packageName.$className"
}
@ -739,12 +753,15 @@ object IR {
/** An import of a polyglot class.
*
* @param entity language-specific information on the imported entity
* @param rename the name this object should be visible under in the
* importing scope
* @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node
*/
sealed case class Polyglot(
entity: Polyglot.Entity,
rename: Option[String],
override val location: Option[IdentifiedLocation],
override val passData: MetadataStorage = MetadataStorage(),
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
@ -755,6 +772,8 @@ object IR {
/** Creates a copy of `this`.
*
* @param entity language-specific information on the imported entity
* @param rename the name this object should be visible under in the
* importing scope
* @param location the source location that the node corresponds to
* @param passData the pass metadata associated with this node
* @param diagnostics compiler diagnostics for this node
@ -763,13 +782,14 @@ object IR {
*/
def copy(
entity: Polyglot.Entity = entity,
rename: Option[String] = rename,
location: Option[IdentifiedLocation] = location,
passData: MetadataStorage = passData,
diagnostics: DiagnosticStorage = diagnostics,
id: Identifier = id
): Polyglot = {
val res =
Polyglot(entity, location, passData, diagnostics)
Polyglot(entity, rename, location, passData, diagnostics)
res.id = id
res
}
@ -795,10 +815,17 @@ object IR {
override def mapExpressions(fn: Expression => Expression): Polyglot =
this
/** Returns the name this object is visible as from Enso code.
*
* @return the visible name of this object
*/
def getVisibleName: String = rename.getOrElse(entity.getVisibleName)
override def toString: String =
s"""
|IR.Module.Scope.Import.Polyglot(
|entity = $entity,
|rename = $rename,
|location = $location,
|passData = ${this.showPassData},
|diagnostics = $diagnostics,
@ -809,7 +836,8 @@ object IR {
override def children: List[IR] = List()
override def showCode(indent: Int): String = {
s"polyglot ${entity.langName} import ${entity.showCode(indent)}"
val renamePart = rename.map(name => s"as $name").getOrElse("")
s"polyglot ${entity.langName} import ${entity.showCode(indent)} $renamePart"
}
}
}

View File

@ -2,7 +2,6 @@ package org.enso.compiler.pass.analyse
import org.enso.compiler.context.{InlineContext, ModuleContext}
import org.enso.compiler.core.IR
import org.enso.compiler.core.IR.Module.Scope.Import.Polyglot
import org.enso.compiler.core.ir.MetadataStorage.ToPair
import org.enso.compiler.data.BindingsMap
import org.enso.compiler.pass.IRPass
@ -51,10 +50,7 @@ case object BindingAnalysis extends IRPass {
}
val importedPolyglot = ir.imports.collect {
case poly: IR.Module.Scope.Import.Polyglot =>
val sym = poly.entity match {
case Polyglot.Java(_, className) => className
}
BindingsMap.PolyglotSymbol(sym)
BindingsMap.PolyglotSymbol(poly.getVisibleName)
}
val moduleMethods = ir.bindings
.collect {

View File

@ -51,6 +51,7 @@ class BindingAnalysisTest extends CompilerTest {
val ir =
"""
|polyglot java import foo.bar.baz.MyClass
|polyglot java import foo.bar.baz.OtherClass as Renamed_Class
|
|type Foo a b c
|type Bar
@ -66,7 +67,7 @@ class BindingAnalysisTest extends CompilerTest {
ir.getMetadata(BindingAnalysis) shouldEqual Some(
BindingsMap(
List(Cons("Foo", 3), Cons("Bar", 0), Cons("Baz", 2)),
List(PolyglotSymbol("MyClass")),
List(PolyglotSymbol("MyClass"), PolyglotSymbol("Renamed_Class")),
List(ModuleMethod("foo")),
ctx.module
)

View File

@ -353,8 +353,11 @@ object Shape extends ShapeImplicit {
isAll: Boolean,
onlyNames: Option[List1[AST.Ident]],
hidingNames: Option[List1[AST.Ident]]
) extends SpacelessAST[T]
final case class JavaImport[T](path: List1[AST.Ident]) extends SpacelessAST[T]
) extends SpacelessAST[T]
final case class JavaImport[T](
path: List1[AST.Ident],
rename: Option[AST.Ident.Cons]
) extends SpacelessAST[T]
final case class Mixfix[T](name: List1[AST.Ident], args: List1[T])
extends SpacelessAST[T]
final case class Group[T](body: Option[T]) extends SpacelessAST[T]
@ -2422,7 +2425,8 @@ object AST {
type JavaImport = ASTOf[Shape.JavaImport]
object JavaImport {
def apply(path: List1[Ident]): JavaImport = Shape.JavaImport[AST](path)
def apply(path: List1[Ident], rename: Option[Ident.Cons]): JavaImport =
Shape.JavaImport[AST](path, rename)
def unapply(t: AST): Option[List1[Ident]] =
Unapply[JavaImport].run(t => t.path)(t)
val any = UnapplyByType[JavaImport]

View File

@ -98,21 +98,30 @@ object Builtin {
val polyglotJavaImport = {
val javaQualName =
Pattern.SepList(Pattern.Cons().or(Pattern.Var()), AST.Opr("."))
val rename = Var("as") :: Pattern.Cons()
Definition(
Var("polyglot") -> Pattern.fromAST(Var("java")),
Var("import") -> javaQualName
Var("import") -> (javaQualName :: rename.opt)
) { ctx =>
ctx.body match {
case List(_, segments) =>
case List(_, nameAndRename) =>
val (nameMatch, renameMatch) =
nameAndRename.body.asInstanceOf[Match.Seq[Shifted[AST]]].elem
val nonDot: List[AST.Ident] =
segments.body.toStream.map(_.wrapped).collect {
nameMatch.toStream.map(_.wrapped).collect {
case AST.Ident.Var.any(v) => v: AST.Ident
case AST.Ident.Cons.any(c) => c: AST.Ident
}
val rename: Option[AST.Ident.Cons] = {
renameMatch.toStream.map(_.wrapped).collectFirst {
case AST.Ident.Cons.any(n) => n
}
}
// The optional unwrap is safe by construction - the pattern
// guarantees at least one Var or Cons in the match result.
AST.JavaImport(
List1.fromListOption(nonDot).getOrElse(internalError)
List1.fromListOption(nonDot).getOrElse(internalError),
rename
)
case _ => internalError
}

View File

@ -6,6 +6,7 @@ polyglot java import java.lang.Integer
polyglot java import java.lang.Float
polyglot java import java.lang.String
polyglot java import java.util.ArrayList
polyglot java import java.lang.StringBuilder as Java_String_Builder
spec = describe "Java FFI" <|
it "should call methods imported from Java" <|
@ -22,4 +23,9 @@ spec = describe "Java FFI" <|
(Integer.sum [1, 2] + 3) . should_equal 6
it "should auto-convert strings across the polyglot boundary" <|
(String.format ["%s bar %s", "baz", "quux"] + " foo").should_equal "baz bar quux foo"
it "should support Java import renaming" <|
builder = Java_String_Builder.new [].to_array
builder.append ["foo"]
builder.append ["bar"]
str = builder.toString []
str.should_equal "foobar"