Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RDS writer #16

Merged
merged 34 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bdd280d
RDSWriter
programLyrique Feb 22, 2024
50f12a6
Add information in the README:
programLyrique Feb 22, 2024
271b2a2
RDSOutputStream
programLyrique Feb 22, 2024
5d4135c
Minimal compiling RDSWriter
programLyrique Mar 5, 2024
0abd2f0
Names of symbol should be non null.
programLyrique Mar 7, 2024
a99082a
Support most common sexp types for RDS writing
programLyrique Mar 11, 2024
a5e6af9
Write read test for integer vector
programLyrique Mar 12, 2024
eb4174c
Test for Logical + associated builder
programLyrique Mar 12, 2024
c634080
Tests for reals and null
programLyrique Mar 12, 2024
a133a1c
Tests writing with GNU R as oracle
programLyrique Mar 13, 2024
f6e6171
ListSXP works.
programLyrique Mar 15, 2024
6a946a6
Supports user environments
programLyrique Mar 19, 2024
f4c144e
Bytecode serialization: WIP
programLyrique Mar 21, 2024
da47ea4
Some stubs I was starting to fill for the bytecode writer
programLyrique May 27, 2024
d95424b
Merge branch 'main' of github.com:PRL-PRG/r-compile-server into rds-w…
breitnw Jun 17, 2024
71bce82
Support for complex number serialization
breitnw Jun 18, 2024
c03929c
Writer and RDS flag refactor, basic GP flag support
breitnw Jun 24, 2024
11f2226
Bytecode serialization should probably work (untested)
breitnw Jun 24, 2024
f80b079
Bytecode serialization actually works (tested)
breitnw Jun 28, 2024
b0aed80
Bytecode serialization actually works (tested)
breitnw Jun 28, 2024
13f1f57
Merge branch 'rds-writer' of github.com:PRL-PRG/r-compile-server into…
breitnw Jun 28, 2024
6a12ca8
Merge branch 'main' of github.com:PRL-PRG/r-compile-server into rds-w…
breitnw Jun 28, 2024
2ec7ca2
Compiler tests passing
breitnw Jul 1, 2024
a54dace
Reference serialization
breitnw Jul 3, 2024
ab9f412
Removed obsolete TODOs
breitnw Jul 3, 2024
dff1661
fixed verification (hopefully)
breitnw Jul 3, 2024
698de95
RDS logging
breitnw Jul 11, 2024
4a7a285
RDS roundtrip test with R closures
breitnw Jul 15, 2024
8ca5da7
Cleaned up file organization
breitnw Jul 16, 2024
9408226
UTF-8 encoding support
breitnw Jul 16, 2024
ce32e0c
Fixed RDSRoundtripTest to compare toString output
breitnw Jul 17, 2024
a441ada
Removed log messages & fixed NA_STRING serialization
breitnw Jul 17, 2024
37e4737
removed logging
breitnw Jul 17, 2024
f2c2e08
Small fixes (formatting, etc.)
breitnw Jul 18, 2024
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
20 changes: 20 additions & 0 deletions src/main/java/org/prlprg/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ public final class AppConfig extends Config {
public static final OptimizationLogLevel OPTIMIZATION_LOG_LEVEL =
get("OPTIMIZATION_LOG_LEVEL", OptimizationLogLevel.NONE);

/**
* How to log RDS reads and writes. Useful for diagnosing misalignments between input and output
* streams.
*
* <p><b>Default:</b>: {@link RDSLogLevel#NONE}
*/
public static final RDSLogLevel RDS_LOG_LEVEL = get("RDS_LOG_LEVEL", RDSLogLevel.NONE);

public enum CfgDebugLevel {
/** No extra checks. */
NONE,
Expand All @@ -74,6 +82,18 @@ public enum OptimizationLogLevel implements Comparable<OptimizationLogLevel> {
/** Log every optimization pass <i>and</i> every inner {@link CodeObject} it's applied to. */
ALL,
}

public enum RDSLogLevel implements Comparable<RDSLogLevel> {
/** Don't log any RDS input or output */
NONE,
/** Only log RDS input and output directly from tests */
TEST,
/**
* Log general RDS input and output, such as from base initialization (WARNING: logging general
* output may cause very large files to be generated, or even lead to out-of-memory errors)
*/
GENERAL,
}
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/prlprg/rds/Flags.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ public int unpackRefIndex() {
return flags >> 8;
}

/**
* Returns a new Flags identical to this one, but with the hasAttr bit set according to
* hasAttributes.
*/
public Flags withAttributes(boolean hasAttributes) {
return new Flags(this.flags & ~ATTR_MASK | (hasAttributes ? ATTR_MASK : 0));
}

/** Returns a new Flags identical to this one, but with the hasTag bit set according to hasTag. */
public Flags withTag(boolean hasTag) {
return new Flags(this.flags & ~TAG_MASK | (hasTag ? TAG_MASK : 0));
}

@Override
public String toString() {
return "Flags{"
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/prlprg/rds/GNURByteCodeEncoderFactory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prlprg.rds;

import com.google.common.primitives.ImmutableIntArray;
import org.jetbrains.annotations.NotNull;
import org.prlprg.bc.Bc;
import org.prlprg.bc.BcCode;
import org.prlprg.bc.BcInstr;
Expand Down Expand Up @@ -37,7 +38,7 @@ public ImmutableIntArray buildRaw() {
}

/** Converts the arguments of the provided BcInstr to a "raw" format; i.e. an array of integers */
public int[] args(BcInstr instr) {
public int[] args(@NotNull BcInstr instr) {
return switch (instr) {
case BcInstr.Goto i -> new int[] {mapping.extract(i.label())};
case BcInstr.BrIfNot i -> new int[] {i.ast().idx(), mapping.extract(i.label())};
Expand Down
121 changes: 32 additions & 89 deletions src/main/java/org/prlprg/rds/RDSInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

class RDSInputStream implements Closeable {
private final DataInputStream in;
private final RDSLogger logger;

RDSInputStream(InputStream in) {
RDSInputStream(InputStream in, RDSLogger logger) {
this.in = new DataInputStream(in);
this.logger = logger;
}

@Override
Expand All @@ -24,114 +26,55 @@ public void close() throws IOException {
* @return the next byte of data, or -1 if the end of the stream is reached.
* @throws IOException if an I/O error occurs.
*/
public int readRaw() throws IOException {
return in.read();
public int readRaw(String desc) throws IOException {
var input = in.read();
breitnw marked this conversation as resolved.
Show resolved Hide resolved
logger.log(input, desc);
programLyrique marked this conversation as resolved.
Show resolved Hide resolved
return input;
}

public byte readByte() throws IOException {
return in.readByte();
public byte readByte(String desc) throws IOException {
var input = in.readByte();
logger.log(input, desc);
return input;
}

public int readInt() throws IOException {
return in.readInt();
public int readInt(String desc) throws IOException {
var input = in.readInt();
logger.log(input, desc);
return input;
}

public double readDouble() throws IOException {
return in.readDouble();
public double readDouble(String desc) throws IOException {
var input = in.readDouble();
logger.log(input, desc);
return input;
}

public String readString(int natEncSize, Charset charset) throws IOException {
public String readString(int natEncSize, Charset charset, String desc) throws IOException {
var buf = new byte[natEncSize];
in.readFully(buf, 0, natEncSize);
return new String(buf, charset);
var input = new String(buf, charset);
logger.log(input, desc);
return input;
}

public int[] readInts(int length) throws IOException {
public int[] readInts(int length, String desc) throws IOException {
int[] ints = new int[length];
for (int i = 0; i < length; i++) {
var n = readInt();
var n = in.readInt();
ints[i] = n;
}
logger.logAll(ints, desc);
return ints;
}

public double[] readDoubles(int length) throws IOException {
double[] ints = new double[length];
public double[] readDoubles(int length, String desc) throws IOException {
double[] doubles = new double[length];
for (int i = 0; i < length; i++) {
var n = readDouble();
ints[i] = n;
var n = in.readDouble();
doubles[i] = n;
}
return ints;
}
}

class RDSInputStreamVerbose extends RDSInputStream {
RDSInputStreamVerbose(InputStream in) {
super(in);
System.out.println("\n===================== READING STREAM ====================\n");
StackTraceElement caller = Thread.currentThread().getStackTrace()[4];
System.out.println("Testing: " + caller + "\n");
}

@Override
public int readRaw() throws IOException {
var res = super.readRaw();
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf("%-20s | reading raw: %d%n", caller.getMethodName(), res);
return res;
}

@Override
public byte readByte() throws IOException {
var res = super.readByte();
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf(
"%-20s | reading byte: %d (%s)%n",
caller.getMethodName(), res, Integer.toBinaryString(res));
return res;
}

@Override
public int readInt() throws IOException {
var res = super.readInt();
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf(
"%-20s | reading int: %d (%s)%n",
caller.getMethodName(), res, Integer.toBinaryString(res));
return res;
}

@Override
public double readDouble() throws IOException {
var res = super.readDouble();
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf("%-20s | reading double: %f%n", caller.getMethodName(), res);
return res;
}

@Override
public String readString(int natEncSize, Charset charset) throws IOException {
var res = super.readString(natEncSize, charset);
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf("%-20s | reading String: %s%n", caller.getMethodName(), res);
return res;
}

@Override
public int[] readInts(int length) throws IOException {
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf("%-20s | reading %d ints...%n", caller.getMethodName(), length);
var res = super.readInts(length);
System.out.printf("%-20s | done reading ints%n", caller.getMethodName());
return res;
}

@Override
public double[] readDoubles(int length) throws IOException {
StackTraceElement caller = Thread.currentThread().getStackTrace()[2];
System.out.printf("%-20s | reading %d doubles...%n", caller.getMethodName(), length);
var res = super.readDoubles(length);
System.out.printf("%-20s | done reading doubles%n", caller.getMethodName());
return res;
logger.logAll(doubles, desc);
return doubles;
}
}
116 changes: 116 additions & 0 deletions src/main/java/org/prlprg/rds/RDSLogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.prlprg.rds;

import java.util.*;
import java.util.function.Supplier;
import java.util.logging.*;
programLyrique marked this conversation as resolved.
Show resolved Hide resolved
import org.prlprg.AppConfig;

final class RDSLogger {
private final StringBuilder output = new StringBuilder();
private final boolean shouldLog;
private int indentLevel;

/**
* @param name a description of the read or write, printed at the start of logging
*/
RDSLogger(String name, AppConfig.RDSLogLevel logLevel) {
this.indentLevel = 0;
this.shouldLog = AppConfig.RDS_LOG_LEVEL.compareTo(logLevel) >= 0;
logString(() -> name);
}

/** Globally enables log output to the provided handler */
public static void addHandler(Handler handler) {
var logger = Logger.getLogger(RDSLogger.class.getName());
logger.addHandler(handler);
handler.setLevel(Level.FINE);
}

/**
* Logs a lazily-evaluated String with the current indent level with Level.FINE.
*
* @param msg a thunk supplying the String to be logged.
*/
private void logString(Supplier<String> msg) {
if (shouldLog) {
int level = this.indentLevel;
String indent = String.join("", Collections.nCopies(level, " "));
output.append(indent).append(msg.get()).append("\n");
}
}

/**
* Logs the reading or writing of a new SEXP by printing an identifier and increasing the
* indentation level by 1
*
* @param sexpIdent a String identifying the SEXP being logged
*/
public void push(String sexpIdent) {
programLyrique marked this conversation as resolved.
Show resolved Hide resolved
logString(() -> sexpIdent + " {");
indentLevel++;
}

/**
* Logs the end of the reading or writing of a SEXP by printing a closing brace and decreasing the
* indentation level by 1
*/
public void pop() {
indentLevel--;
logString(() -> "}");
}

/**
* Lazily logs a value read from the input stream or written to the output stream, alongside a
* description of the value's usage
*
* @param <T> the type of the value to be logged
* @param value the value to be logged
* @param description a description of the value's usage
*/
public <T> void log(T value, String description) {
logString(
() ->
switch (value) {
case Integer i ->
String.format("%s: %d (%s)", description, i, Integer.toBinaryString(i));
case Byte b ->
String.format("%s: %d (%s)", description, b, Integer.toBinaryString(b));
default -> String.format("%s: %s", description, value);
});
}

/**
* Lazily logs an array of ints read from the input stream or written to the output stream,
* alongside a description of the array's usage
*
* @param ints the array to be logged
* @param description a description of the array's usage
*/
public void logAll(int[] ints, String description) {
logString(() -> String.format("%s: %s", description, Arrays.toString(ints)));
}

/**
* Lazily logs an array of doubles read from the input stream or written to the output stream,
* alongside a description of the array's usage
*
* @param doubles the array to be logged
* @param description a description of the array's usage
*/
public void logAll(double[] doubles, String description) {
logString(() -> String.format("%s: %s", description, Arrays.toString(doubles)));
}

/**
* Writes the RDS output collected thus far to the system logger at level {@code Level.FINE}. This
* is written in one go rather than in individual messages to preserve formatting and reduce
* metadata stored with each message.
*/
public void finish() {
if (shouldLog) {
Logger logger = Logger.getLogger(RDSLogger.class.getName());
logger.setLevel(Level.FINE);
logger.fine(output.toString());
}
}
}
Loading
Loading