From b4c0d0817c82a7192a604bc6f1ec59d31250ea01 Mon Sep 17 00:00:00 2001 From: jakobeha Date: Thu, 8 Aug 2024 19:58:41 +0200 Subject: [PATCH] @WIP IR: rebase fixes --- src/main/java/org/prlprg/rds/RDSReader.java | 18 ++++++-- src/main/java/org/prlprg/rds/RDSWriter.java | 50 +++++++++++---------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/prlprg/rds/RDSReader.java b/src/main/java/org/prlprg/rds/RDSReader.java index 063ed4c79..44a29c614 100644 --- a/src/main/java/org/prlprg/rds/RDSReader.java +++ b/src/main/java/org/prlprg/rds/RDSReader.java @@ -37,6 +37,7 @@ import org.prlprg.sexp.StrSXP; import org.prlprg.sexp.TaggedElem; import org.prlprg.sexp.UserEnvSXP; +import org.prlprg.sexp.ValueSXP; import org.prlprg.sexp.VecSXP; import org.prlprg.util.IO; @@ -204,12 +205,21 @@ private SEXP readPromise(Flags flags) throws IOException { if (tag instanceof NilSXP) { // If the tag is nil, the promise is eager. We put `EMPTY_ENV` instead of nil as the promise // environment (it can be anything, since it's only used for lazy evaluation). - return new PromSXP<>(expr, val, SEXPs.EMPTY_ENV); + if (val == null) { + throw new RDSException( + "Promise serialized with nil tag but no value (so it's lazy, but evaluating in GNU-R would crash because the environment is `NULL`)"); + } + if (!(val instanceof ValueSXP v)) { + throw new RDSException("Nested promise"); + } + return new PromSXP<>(expr, v, SEXPs.EMPTY_ENV); } else if (tag instanceof EnvSXP env) { // Otherwise, the tag should be an environment, the promise's environment, and the promise is // lazy. The "value" of a lazy promise is "unbound", which we represent as `null`. - assert val == null - : "promise serialized with env but not unbound value (so the environment is redundant, GNU-R doesn't serialize promises like this)"; + if (val != null) { + throw new RDSException( + "Promise serialized with env but not unbound value (so the environment is redundant, GNU-R doesn't serialize promises like this)"); + } return new PromSXP<>(expr, null, env); } else { throw new RDSException("Expected promise ENV to be environment"); @@ -601,7 +611,7 @@ private ListSXP readList(Flags flags) throws IOException { flags = readFlags(); } - return SEXPs.list(data.build(), attributes); + return SEXPs.list(data.build(), Objects.requireNonNull(attributes)); } private CloSXP readClosure(Flags flags) throws IOException { diff --git a/src/main/java/org/prlprg/rds/RDSWriter.java b/src/main/java/org/prlprg/rds/RDSWriter.java index a12ca2c82..1dbd19225 100644 --- a/src/main/java/org/prlprg/rds/RDSWriter.java +++ b/src/main/java/org/prlprg/rds/RDSWriter.java @@ -90,6 +90,7 @@ public void writeItemOrUnbound(@Nullable SEXP s) throws IOException { case RDSItemType.Special special -> { switch (special) { case RDSItemType.Special.NAMESPACESXP -> { + Objects.requireNonNull(s); // add to the ref table refAdd(s); // write details about the namespace @@ -111,10 +112,13 @@ public void writeItemOrUnbound(@Nullable SEXP s) throws IOException { case RDSItemType.Sexp _ -> { // Otherwise, write the sexp as normal switch (s) { - case SymSXP sym -> writeSymbol(sym); + case RegSymSXP sym -> writeRegSymbol(sym); + case MissingSXP _ -> throw new UnsupportedOperationException("can't directly serialize the missing value"); + case null -> throw new UnsupportedOperationException("can't serialize the unbound value"); case EnvSXP env -> writeEnv(env); case ListSXP list -> writeListSXP(list); case LangSXP lang -> writeLangSXP(lang); + case DotsListSXP _ -> throw new UnsupportedOperationException("can't directly serialize a dots list"); case PromSXP prom -> writePromSXP(prom); case CloSXP clo -> writeCloSXP(clo); case BuiltinOrSpecialSXP bos -> writeBuiltinOrSpecialSXP(bos); @@ -174,7 +178,7 @@ private RDSItemType rdsType(@Nullable SEXP s) { case BaseEnvSXP _ -> RDSItemType.Special.BASEENV_SXP; case GlobalEnvSXP _ -> RDSItemType.Special.GLOBALENV_SXP; // Non-"Save Special" cases - case NamespaceEnvSXP ns -> RDSItemType.Special.NAMESPACESXP; + case NamespaceEnvSXP _ -> RDSItemType.Special.NAMESPACESXP; case MissingSXP _ -> RDSItemType.Special.MISSINGARG_SXP; case null -> RDSItemType.Special.UNBOUNDVALUE_SXP; default -> new RDSItemType.Sexp(s.type()); @@ -309,19 +313,14 @@ private void writeEnv(EnvSXP env) throws IOException { } } - private void writeSymbol(SymSXP s) throws IOException { - switch (s) { - case RegSymSXP rs -> { - // Add to the ref table - refAdd(rs); - // Write the symbol - writeChars(rs.name()); - } - case MissingSXP _ -> throw new UnsupportedOperationException("can't directly serialize the missing value"); - } + private void writeRegSymbol(RegSymSXP rs) throws IOException { + // Add to the ref table + refAdd(rs); + // Write the symbol + writeChars(rs.name()); } - private void writeBuiltinOrSpecialSXP(BuiltinOrSpecialSXP bos) throws IOException { + private void writeBuiltinOrSpecialSXP(BuiltinOrSpecialSXP bos) { // For now, we throw an exception upon writing any SpecialSXP or BuiltinSXP. This is because // RDS serializes builtins via their name, but we do not have any (fully implemented) construct // representing the name of a builtin (instead, they are represented with indices) @@ -464,16 +463,18 @@ private void scanForCircles(SEXP sexp, HashMap reps, HashSet scanForCircles(el, reps, seen)); } - case ListSXP list -> { - // For ListSXP, we scan the values - list.values().forEach((el) -> scanForCircles(el, reps, seen)); + // For ListSXP, we scan the values + case ListSXP list -> + list.values().forEach((el) -> scanForCircles(el, reps, seen)); + case DotsListSXP _ -> { + // Do nothing } } } - case BCodeSXP bc -> { - // For bytecode, we scan the constant pool + // For bytecode, we scan the constant pool + case BCodeSXP bc -> bc.bc().consts().forEach((el) -> scanForCircles(el, reps, seen)); - } + default -> { // do nothing } @@ -512,8 +513,9 @@ private void writeByteCodeLang(SEXP s, HashMap reps, AtomicIntege if (lol.hasAttributes()) { type = switch (lol) { - case LangSXP _lang -> RDSItemType.Special.ATTRLANGSXP; - case ListSXP _list -> RDSItemType.Special.ATTRLISTSXP; + case LangSXP _ -> RDSItemType.Special.ATTRLANGSXP; + case ListSXP _ -> RDSItemType.Special.ATTRLISTSXP; + case DotsListSXP _ -> throw new UnreachableError(); }; } out.writeInt(type.i()); @@ -543,6 +545,7 @@ private void writeByteCodeLang(SEXP s, HashMap reps, AtomicIntege // write tail writeByteCodeLang(list.subList(1), reps, nextRepIndex); } + case DotsListSXP _ -> throw new UnreachableError(); } } else { // Print a zero as padding and write the item normally out.writeInt(0); @@ -577,10 +580,9 @@ private void writeByteCodeConsts( out.writeInt(c.type().i); writeByteCode1(bc, reps, nextRepIndex); } - case AbstractPairListSXP l -> { - // writeBCLang writes the type i + // writeBCLang writes the type i + case AbstractPairListSXP l -> writeByteCodeLang(l, reps, nextRepIndex); - } default -> { out.writeInt(c.type().i); writeItem(c);