Skip to content
Open
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
5 changes: 3 additions & 2 deletions Project/src/main/java/prlprg/GenDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ public Ty fixUp(List<TyVar> bounds) {
switch (nm()) {
case "typeof": // typeof is a special case
return new TyCon(toString());
case "Nothing":
return Ty.None;
// TODO: Core.TypeofBottom and Nothing are different!
// case "Nothing":
// return Ty.None;
default:
if (!pre_tydb.containsKey(nm)) {
System.err.println("Warning: " + nm + " not found in type database, patching");
Expand Down
57 changes: 53 additions & 4 deletions Project/src/main/java/prlprg/Generator.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,37 @@ boolean isGround() {
public String toString() {
return "function " + nm + " " + ty;
}

public void add_pkgs(HashSet<String> pkgs) {
if (nm.contains(".")) {
pkgs.add(nm.split("\\.", 2)[0]);
}
if (ty instanceof Tuple t) {
for (var typ : t.tys) {
// TODO: recurse into other kinds of type
if (typ instanceof Inst inst) {
if (inst.nm().contains(".")) {
pkgs.add(inst.nm().split("\\.", 2)[0]);
}
}
}
}
}

public String juliaName() {
// FileWatching.|
// Pkg.Artifacts.var#verify_artifact#1
// Base.GMP.MPQ.//
var parts = nm.split("\\.");
var name = parts[parts.length - 1];
if (name.startsWith("var#")) {
name = name.substring(0, 3) + "\"" + name.substring(3) + "\"";
} else if (!Character.isLetter(name.charAt(0)) && name.charAt(0) != '_') {
name = ":(" + name + ")";
}
parts[parts.length - 1] = name;
return String.join(".", parts);
}
}

GenDB db; // our database of types and signatures -- these are still not complete
Expand Down Expand Up @@ -310,17 +341,35 @@ public void gen() {
}

try {
var w = new BufferedWriter(new FileWriter("test.jl"));
HashSet<String> pkgs = new HashSet<>();
var w = new BufferedWriter(new FileWriter("tests.jl"));
for (var b : sigs.values()) {
for (var s : b) {
if (s.isGround()) {
System.err.println("Ground: " + s);
var str = ((Tuple) s.ty).tys.stream().map(Type::toString).collect(Collectors.joining(","));
var content = "code_warntype(" + s.nm + ",[" + str + "])\n";
w.write(content);

s.add_pkgs(pkgs);

w.write("isstablecall(");
w.write(s.juliaName());
w.write(", [");
w.write(((Tuple) s.ty).tys.stream().map(Type::toString).collect(Collectors.joining(", ")));
w.write("])\n");
}
}
}
w.close();

pkgs.remove("Base");
pkgs.remove("Core");
var pkgsf = new BufferedWriter(new FileWriter("pkgs.txt"));
var importsf = new BufferedWriter(new FileWriter("imports.jl"));
for (var p : pkgs) {
pkgsf.write(p + "\n");
importsf.write("import " + p + "\n");
}
pkgsf.close();
importsf.close();
} catch (IOException e) {
throw new Error(e);
}
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
In the root of this repo (e.g. `~/Projects/JuliaGenerable`):

Generate function and type records

julia --project=./TypeDiscover.jl -e 'using TypeDiscover; typediscover(; funcfile="./Inputs/stdf.jlg", typefile="./Inputs/stdt.jlg")'

Build the Java project

mvn -f ./Project/pom.xml package

Generate the test cases

java -cp ./Project/target/JuliaGenerable2-1.0-SNAPSHOT.jar prlprg.App -c=NONE -r=./Inputs/ -f=stdf.jlg -t=stdt.jlg -h=FALSE -s=FALSE

Install dependencies in a shared Julia environment (this stays between runs and can be reused)

julia ./StabilityChecker/shenv.jl ./pkgs.txt

And run the tests

julia ./StabilityChecker/test.jl ./imports.jl ./tests.jl

If all goes well, you should see results such as

Results: Passed 100.0% of 5473 tests
PASSED: 5473
FAILED: 0
STABLE: 3608
UNSTABLE: 1865

> TODO: there are some bugs in the generated tests:
>
> `isstablecall(Base, [])`
> `isstablecall(Core.Compiler, [])`
> `isstablecall(Core.Compiler.merge, [NamedTuple, NamedTuple{EOF}])` and such should replace EOFs with `()`
> `isstablecall(Base.iterate, [Base.Iterators.ProductIterator{()}])` should take `Tuple{}` not `()`
> `isstablecall(Core.Compiler.iterate, [Core.Compiler.Iterators.ProductIterator{()}])` same
> `isstablecall(Core.Compiler.lift_comparison!, [typeof(===), Core.Compiler.IncrementalCompact, Int64, Expr, Core.Compiler.IdDict{Pair{[Core.Compiler.NewSSAValue|Core.Compiler.OldSSAValue|Core.SSAValue],Any},[Core.Compiler.NewSSAValue|Core.Compiler.OldSSAValue|Core.SSAValue]}])` and such printing unions
>
> For now, these need to be commented out manually.
12 changes: 12 additions & 0 deletions StabilityChecker/shenv.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pkgs = isempty(ARGS) ? error("Required argument: path of pkgs.txt") : joinpath(pwd(), popfirst!(ARGS))

const env = "JuliaGenerable"

using Pkg

Pkg.activate(env; shared=true)
Pkg.add(split(strip(read(pkgs, String)), "\n"))
Pkg.instantiate()

@info "Shared environment `@$env' at `$(joinpath(Pkg.envdir(), env))':"
Pkg.status()
53 changes: 53 additions & 0 deletions StabilityChecker/test.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
imports = isempty(ARGS) ? error("Required argument: path of imports.jl") : joinpath(pwd(), popfirst!(ARGS))
tests = isempty(ARGS) ? error("Required argument: path of tests.jl") : joinpath(pwd(), popfirst!(ARGS))

const env = "JuliaGenerable"

import Pkg

Pkg.activate(env; shared=true)

mutable struct Stats
ok::Int
fail::Int
stable::Int
unstable::Int
end
Stats() = Stats(0, 0, 0, 0)
Base.show(io::IO, stats::Stats) = begin
total = stats.ok + stats.fail
println(io, "Passed $(stats.ok / total * 100.0)% of $total tests")
println(io, " PASSED: $(stats.ok)")
println(io, " FAILED: $(stats.fail)")
println(io, " STABLE: $(stats.stable)")
println(io, " UNSTABLE: $(stats.unstable)")
end
const stats = Stats()

isstablecall(@nospecialize(f::Function), @nospecialize(ts::Vector)) = begin
try
ct = code_typed(f, (ts...,), optimize=false)
length(ct) == 1 || return false
(_, t) = first(ct)
res = isconcretetype(t)
global stats.ok += 1
if res
global stats.stable += 1
else
global stats.unstable += 1
end
return res
catch e
println("ERROR:");
showerror(stdout, e);
println();
global stats.fail += 1;
end
end

include(imports)

println("Running tests from `$tests'")
include(tests)

println("Results: $stats")
13 changes: 9 additions & 4 deletions TypeDiscover.jl/src/TypeDiscover.jl
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,17 @@ end
baseShowMethodCustom(io::IO, m::Method, kind::String) = begin
tv, decls, file, line = Base.arg_decl_parts(m)
sig = Base.unwrap_unionall(m.sig)
mod = try
first(sig.parameters).name.module
catch _
m.module
end
if sig === Tuple
# Builtin
print(io, m.module, ".", m.name, "(...) [", kind, "]")
print(io, mod, ".", m.name, "(...) [", kind, "]")
return
end
print(io, m.module, ".", decls[1][2], "(")
print(io, mod, ".", decls[1][2], "(")
join(
io,
String[isempty(d[2]) ? d[1] : string(d[1], "::", d[2]) for d in decls[2:end]],
Expand Down Expand Up @@ -356,8 +361,8 @@ typediscover(mods::AbstractVector{Module}=Base.loaded_modules_array();
typefile=nothing,
skip_macros=true,
skip_lambdas=true,
skip_constructors=false,
skip_callable=false,
skip_constructors=true,
skip_callable=true,
skip_builtins=true,
skip_intrinsics=true,
skip_generics=false,
Expand Down