Fix globbed read/import bugs (#449)

* Split MemberNode into (Regular/Shared)MemberNode

SharedMemberNode enables generating non-constant object members
at run time without generating an unbounded number of Truffle root nodes.

* Invert shouldRunTypeCheck to match its name

* Introduce VmObjectBuilder

Introduce VmObjectBuilder, a uniform way to build `VmObject`s
whose `ObjectMember`s are determined at run time.
Replace some manual object building code with VmObjectBuilder.
Add some assertions to fix IntelliJ warnings.

* Improve implementation of globbed read/import nodes

- Leverage SharedMemberNode to have a single Truffle root node per globbed
  read/import expression instead of one root node per resolved glob element.
- Remove caching in ReadGlobNode because it only works correctly if glob pattern is a string *constant*.
- Remove caching in StaticReadNode (now ReadGlobElementNode/ImportGlobElementNode)
  because it seems unnecessary and the implementation doesn't look quite right.

* Simplify code

* Fix ClassCastException when reflecting on globbed import

* Fix caching of globbed reads

Problem:
The result of a globbed read depends not only on the glob pattern but also on the current module URI.
However, ResourceManager does not take this into account when caching globbed reads, causing wrong results.

Changes:
- Cache globbed reads per read expression.
  This is also how globbed imports are cached, except that import URIs are constant.
- Make ReadGlobNode and ImportGlobNode code as similar as possible.
- Reduce code duplication by inheriting from AbstractReadNode.
- Add some tests.
This commit is contained in:
translatenix
2024-06-03 17:10:56 -07:00
committed by GitHub
parent d81a12352c
commit 207d0c78f0
39 changed files with 724 additions and 503 deletions
@@ -0,0 +1,2 @@
normalRead = read("resource.txt")
globbedRead = read*("*.txt")
@@ -0,0 +1 @@
child resource
@@ -0,0 +1,2 @@
normalRead = read("resource.txt")
globbedRead = read*("*.txt")
@@ -0,0 +1 @@
resource
@@ -22,14 +22,32 @@ examples {
}
["read file"] {
read("read.pkl")
read("globtest/file1.txt")
module.catch(() -> read("other.txt"))
read?("read.pkl")
read?("globtest/file1.txt")
read?("other.txt")
}
["read triple-dot file"] {
read(".../input-helper/basic/read/resource.txt")
read?(".../input-helper/basic/read/resource.txt")
}
["read non-allowed resource"] {
module.catch(() -> read("forbidden:resource"))
module.catch(() -> read?("forbidden:resource"))
}
["use read expression with non-constant resource URI"] {
local function doRead(uri) = read(uri)
doRead("globtest/file1.txt")
doRead("globtest/file2.txt")
}
["read different resources with same relative resource URI"] {
local module1 = import(".../input-helper/basic/read/module1.pkl")
local module2 = import(".../input-helper/basic/read/child/module2.pkl")
module1.normalRead
module2.normalRead
}
}
@@ -30,4 +30,18 @@ examples {
["package:"] {
read*("package://localhost:0/birds@0.5.0#/**.pkl")
}
["use read expression with non-constant glob pattern"] {
local function doRead(pattern) = read*(pattern)
doRead("globtest/file*.txt")
doRead("globtest/file1.txt")
doRead("globtest/file2.txt")
}
["read different resources with same glob pattern"] {
local module1 = import(".../input-helper/basic/read/module1.pkl")
local module2 = import(".../input-helper/basic/read/child/module2.pkl")
module1.globbedRead
module2.globbedRead
}
}
@@ -17,94 +17,80 @@ examples {
}
["read file"] {
new {
uri = "file:///$snippetsDir/input/basic/read.pkl"
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
text = """
amends "../snippetTest.pkl"
examples {
["read env variable"] {
read("env:NAME1")
read("env:NAME2")
module.catch(() -> read("env:OTHER"))
read?("env:NAME1")
read?("env:NAME2")
read?("env:OTHER")
}
["read external property"] {
read("prop:name1")
read("prop:name2")
module.catch(() -> read("prop:other"))
read?("prop:name1")
read?("prop:name2")
read?("prop:other")
}
["read file"] {
read("read.pkl")
module.catch(() -> read("other.txt"))
read?("read.pkl")
read?("other.txt")
}
["read non-allowed resource"] {
module.catch(() -> read("forbidden:resource"))
module.catch(() -> read?("forbidden:resource"))
}
}
file1
"""
base64 = "YW1lbmRzICIuLi9zbmlwcGV0VGVzdC5wa2wiCgpleGFtcGxlcyB7CiAgWyJyZWFkIGVudiB2YXJpYWJsZSJdIHsKICAgIHJlYWQoImVudjpOQU1FMSIpCiAgICByZWFkKCJlbnY6TkFNRTIiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImVudjpPVEhFUiIpKQoKICAgIHJlYWQ/KCJlbnY6TkFNRTEiKQogICAgcmVhZD8oImVudjpOQU1FMiIpCiAgICByZWFkPygiZW52Ok9USEVSIikKICB9CgogIFsicmVhZCBleHRlcm5hbCBwcm9wZXJ0eSJdIHsKICAgIHJlYWQoInByb3A6bmFtZTEiKQogICAgcmVhZCgicHJvcDpuYW1lMiIpCiAgICBtb2R1bGUuY2F0Y2goKCkgLT4gcmVhZCgicHJvcDpvdGhlciIpKQoKICAgIHJlYWQ/KCJwcm9wOm5hbWUxIikKICAgIHJlYWQ/KCJwcm9wOm5hbWUyIikKICAgIHJlYWQ/KCJwcm9wOm90aGVyIikKICB9CgogIFsicmVhZCBmaWxlIl0gewogICAgcmVhZCgicmVhZC5wa2wiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoIm90aGVyLnR4dCIpKQogICAgcmVhZD8oInJlYWQucGtsIikKICAgIHJlYWQ/KCJvdGhlci50eHQiKQogIH0KCiAgWyJyZWFkIG5vbi1hbGxvd2VkIHJlc291cmNlIl0gewogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImZvcmJpZGRlbjpyZXNvdXJjZSIpKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQ/KCJmb3JiaWRkZW46cmVzb3VyY2UiKSkKICB9Cn0K"
base64 = "ZmlsZTEK"
}
"Cannot find resource `other.txt`."
new {
uri = "file:///$snippetsDir/input/basic/read.pkl"
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
text = """
amends "../snippetTest.pkl"
examples {
["read env variable"] {
read("env:NAME1")
read("env:NAME2")
module.catch(() -> read("env:OTHER"))
read?("env:NAME1")
read?("env:NAME2")
read?("env:OTHER")
}
["read external property"] {
read("prop:name1")
read("prop:name2")
module.catch(() -> read("prop:other"))
read?("prop:name1")
read?("prop:name2")
read?("prop:other")
}
["read file"] {
read("read.pkl")
module.catch(() -> read("other.txt"))
read?("read.pkl")
read?("other.txt")
}
["read non-allowed resource"] {
module.catch(() -> read("forbidden:resource"))
module.catch(() -> read?("forbidden:resource"))
}
}
file1
"""
base64 = "YW1lbmRzICIuLi9zbmlwcGV0VGVzdC5wa2wiCgpleGFtcGxlcyB7CiAgWyJyZWFkIGVudiB2YXJpYWJsZSJdIHsKICAgIHJlYWQoImVudjpOQU1FMSIpCiAgICByZWFkKCJlbnY6TkFNRTIiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImVudjpPVEhFUiIpKQoKICAgIHJlYWQ/KCJlbnY6TkFNRTEiKQogICAgcmVhZD8oImVudjpOQU1FMiIpCiAgICByZWFkPygiZW52Ok9USEVSIikKICB9CgogIFsicmVhZCBleHRlcm5hbCBwcm9wZXJ0eSJdIHsKICAgIHJlYWQoInByb3A6bmFtZTEiKQogICAgcmVhZCgicHJvcDpuYW1lMiIpCiAgICBtb2R1bGUuY2F0Y2goKCkgLT4gcmVhZCgicHJvcDpvdGhlciIpKQoKICAgIHJlYWQ/KCJwcm9wOm5hbWUxIikKICAgIHJlYWQ/KCJwcm9wOm5hbWUyIikKICAgIHJlYWQ/KCJwcm9wOm90aGVyIikKICB9CgogIFsicmVhZCBmaWxlIl0gewogICAgcmVhZCgicmVhZC5wa2wiKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoIm90aGVyLnR4dCIpKQogICAgcmVhZD8oInJlYWQucGtsIikKICAgIHJlYWQ/KCJvdGhlci50eHQiKQogIH0KCiAgWyJyZWFkIG5vbi1hbGxvd2VkIHJlc291cmNlIl0gewogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQoImZvcmJpZGRlbjpyZXNvdXJjZSIpKQogICAgbW9kdWxlLmNhdGNoKCgpIC0+IHJlYWQ/KCJmb3JiaWRkZW46cmVzb3VyY2UiKSkKICB9Cn0K"
base64 = "ZmlsZTEK"
}
null
}
["read triple-dot file"] {
new {
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
text = """
resource
"""
base64 = "cmVzb3VyY2UK"
}
new {
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
text = """
resource
"""
base64 = "cmVzb3VyY2UK"
}
}
["read non-allowed resource"] {
"Refusing to read resource `forbidden:resource` because it does not match any entry in the resource allowlist (`--allowed-resources`)."
"Refusing to read resource `forbidden:resource` because it does not match any entry in the resource allowlist (`--allowed-resources`)."
}
["use read expression with non-constant resource URI"] {
new {
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
text = """
file1
"""
base64 = "ZmlsZTEK"
}
new {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
text = """
file2
"""
base64 = "ZmlsZTIK"
}
}
["read different resources with same relative resource URI"] {
new {
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
text = """
resource
"""
base64 = "cmVzb3VyY2UK"
}
new {
uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt"
text = """
child resource
"""
base64 = "Y2hpbGQgcmVzb3VyY2UK"
}
}
}
@@ -176,4 +176,66 @@ examples {
}
}
}
["use read expression with non-constant glob pattern"] {
new {
["globtest/file1.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
text = """
file1
"""
base64 = "ZmlsZTEK"
}
["globtest/file2.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
text = """
file2
"""
base64 = "ZmlsZTIK"
}
}
new {
["globtest/file1.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file1.txt"
text = """
file1
"""
base64 = "ZmlsZTEK"
}
}
new {
["globtest/file2.txt"] {
uri = "file:///$snippetsDir/input/basic/globtest/file2.txt"
text = """
file2
"""
base64 = "ZmlsZTIK"
}
}
}
["read different resources with same glob pattern"] {
new {
["resource.txt"] {
uri = "file:///$snippetsDir/input-helper/basic/read/resource.txt"
text = """
resource
"""
base64 = "cmVzb3VyY2UK"
}
}
new {
["resource.txt"] {
uri = "file:///$snippetsDir/input-helper/basic/read/child/resource.txt"
text = """
child resource
"""
base64 = "Y2hpbGQgcmVzb3VyY2UK"
}
}
}
}