Skip to content

Commit

Permalink
Showing 15 changed files with 832 additions and 791 deletions.
6 changes: 6 additions & 0 deletions truffle/pom.rb
Original file line number Diff line number Diff line change
@@ -11,11 +11,17 @@
'jruby.basedir' => '${basedir}/..' )

jar 'org.yaml:snakeyaml:1.14'
jar 'org.antlr:antlr4-runtime:4.5'

jar 'org.jruby:jruby-core', '${project.version}', :scope => 'provided'

jar 'com.oracle:truffle:0.7'
jar 'com.oracle:truffle-dsl-processor:0.7', :scope => 'provided'

plugin 'org.antlr:antlr4-maven-plugin', '4.5' do
execute_goal :antlr4
end

plugin( :compiler,
'encoding' => 'utf-8',
'debug' => 'true',
17 changes: 17 additions & 0 deletions truffle/pom.xml
Original file line number Diff line number Diff line change
@@ -27,6 +27,11 @@ DO NOT MODIFIY - GENERATED CODE
<artifactId>snakeyaml</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-core</artifactId>
@@ -57,6 +62,18 @@ DO NOT MODIFIY - GENERATED CODE
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.5</version>
<executions>
<execution>
<goals>
<goal>antlr4</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
74 changes: 74 additions & 0 deletions truffle/src/main/antlr4/org/jruby/truffle/format/parser/Pack.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
grammar Pack;

sequence : directive* ;

directive : C count? # character
| (S NATIVE? LITTLE | 'v') count? # shortLittle
| (S NATIVE? BIG | 'n') count? # shortBig
| S NATIVE? count? # shortNative
| I (LITTLE NATIVE? | NATIVE? LITTLE) count? # intLittle
| I (BIG NATIVE? | NATIVE? BIG) count? # intBig
| I NATIVE? count? # intNative
| (L LITTLE | 'V') count? # longLittle
| (L BIG | 'N') count? # longBig
| L count? # longNative
| (Q | L NATIVE) LITTLE count? # quadLittle
| (Q | L NATIVE) BIG count? # quadBig
| (Q | L NATIVE) count? # quadNative
| 'U' count? # utf8Character
| 'w' count? # berInteger
| D count? # doubleNative
| F count? # floatNative
| 'E' count? # doubleLittle
| 'e' count? # floatLittle
| 'G' count? # doubleBig
| 'g' count? # floatBig
| 'A' count? # binaryStringSpacePadded
| 'a' count? # binaryStringNullPadded
| 'Z' count? # binaryStringNullStar
| 'B' count? # bitStringMSBFirst
| 'b' count? # bitStringMSBLast
| 'H' count? # hexStringHighFirst
| 'h' count? # hexStringLowFirst
| 'u' count? # uuString
| 'M' INT? # mimeString
| 'm' count? # base64String
| ('p' | 'P') # pointer
| '@' INT? # at
| 'X' count? # back
| 'x' count? # nullByte
| subSequence # subSequenceAlternate
| ('v' | 'n' | 'V' | 'N' | 'U' | 'w' | D |
F | 'E' | 'e' | 'g' | 'G' | 'A' | 'a' |
'Z' | 'B' | 'b' | 'H' | 'h' | 'u' | 'M' |
'm' | 'p' | 'P' | 'X' | 'x') NATIVE #errorDisallowedNative ;

subSequence : '(' directive+ ')' INT? ;

count : INT | '*' ;

C : [cC] ;
S : [sS] ;
I : [iI] ;
L : [lL] ;
Q : [qQ] ;
D : [dD] ;
F : [fF] ;

LITTLE : '<' ;
BIG : '>' ;
NATIVE : [_!] ;

INT : [0-9]+ ;

WS : [ \t\n\u000b\f\r\u0000]+ -> skip ;
COMMENT : '#' .*? (('\r'? '\n') | EOF) -> skip ;
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
* binary string. I think MRI that now has a copying collector so not sure how
* safe this is even there.
* <p>
* We simply implement it as {@code NULL}. At least any attempty to dereference
* We simply implement it as {@code NULL}. At least any attempt to dereference
* will fail early.
* <pre>
* [1, 2, 3].pack('x') # => "\x00\x00\x00\x00\x00\x00\x00\x00"
Original file line number Diff line number Diff line change
@@ -52,6 +52,8 @@ public Object write(VirtualFrame frame, Object nil) {
for (int n = 0; n < width; n++) {
writeByte(frame, padding);
}
} else if (appendNull) {
writeByte(frame, (byte) 0);
}

return null;
Original file line number Diff line number Diff line change
@@ -14,10 +14,11 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.Endianness;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.util.ByteList;

import java.nio.ByteOrder;

/**
* Read a string that contains a binary string (literally a string of binary
* digits written using 1 and 0 characters) and write as actual binary data.
@@ -29,13 +30,13 @@
})
public abstract class WriteBitStringNode extends PackNode {

private final Endianness endianness;
private final ByteOrder byteOrder;
private final boolean star;
private final int length;

public WriteBitStringNode(RubyContext context, Endianness endianness, boolean star, int length) {
public WriteBitStringNode(RubyContext context, ByteOrder byteOrder, boolean star, int length) {
super(context);
this.endianness = endianness;
this.byteOrder = byteOrder;
this.star = star;
this.length = length;
}
@@ -62,49 +63,45 @@ public Object write(VirtualFrame frame, ByteList bytes) {
occurrences = lCurElemString.length();
}

switch (endianness) {
case LITTLE: {
for (int i = 0; i < occurrences;) {
if ((lCurElemString.charAt(i++) & 1) != 0) {//if the low bit is set
currentByte |= 128; //set the high bit of the result
}

if ((i & 7) == 0) {
writeByte(frame, (byte) (currentByte & 0xff));
currentByte = 0;
continue;
}

//if the index is not a multiple of 8, we are not on a byte boundary
currentByte >>= 1; //shift the byte
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
for (int i = 0; i < occurrences;) {
if ((lCurElemString.charAt(i++) & 1) != 0) {//if the low bit is set
currentByte |= 128; //set the high bit of the result
}

if ((occurrences & 7) != 0) { //if the length is not a multiple of 8
currentByte >>= 7 - (occurrences & 7); //we need to pad the last byte
if ((i & 7) == 0) {
writeByte(frame, (byte) (currentByte & 0xff));
currentByte = 0;
continue;
}
} break;

case BIG: {
for (int i = 0; i < occurrences;) {
currentByte |= lCurElemString.charAt(i++) & 1;

// we filled up current byte; append it and create next one
if ((i & 7) == 0) {
writeByte(frame, (byte) (currentByte & 0xff));
currentByte = 0;
continue;
}
//if the index is not a multiple of 8, we are not on a byte boundary
currentByte >>= 1; //shift the byte
}

//if the index is not a multiple of 8, we are not on a byte boundary
currentByte <<= 1;
}
if ((occurrences & 7) != 0) { //if the length is not a multiple of 8
currentByte >>= 7 - (occurrences & 7); //we need to pad the last byte
writeByte(frame, (byte) (currentByte & 0xff));
}
} else {
for (int i = 0; i < occurrences;) {
currentByte |= lCurElemString.charAt(i++) & 1;

if ((occurrences & 7) != 0) { //if the length is not a multiple of 8
currentByte <<= 7 - (occurrences & 7); //we need to pad the last byte
// we filled up current byte; append it and create next one
if ((i & 7) == 0) {
writeByte(frame, (byte) (currentByte & 0xff));
currentByte = 0;
continue;
}
} break;

//if the index is not a multiple of 8, we are not on a byte boundary
currentByte <<= 1;
}

if ((occurrences & 7) != 0) { //if the length is not a multiple of 8
currentByte <<= 7 - (occurrences & 7); //we need to pad the last byte
writeByte(frame, (byte) (currentByte & 0xff));
}
}

writeNullBytes(frame, padLength);
Original file line number Diff line number Diff line change
@@ -14,10 +14,11 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.Endianness;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.util.ByteList;

import java.nio.ByteOrder;

/**
* Read a string that contains a hex string and write as actual binary data.
* <pre>
@@ -28,12 +29,12 @@
})
public abstract class WriteHexStringNode extends PackNode {

private final Endianness endianness;
private final ByteOrder byteOrder;
private final int length;

public WriteHexStringNode(RubyContext context, Endianness endianness, int length) {
public WriteHexStringNode(RubyContext context, ByteOrder byteOrder, int length) {
super(context);
this.endianness = endianness;
this.byteOrder = byteOrder;
this.length = length;
}

@@ -61,33 +62,24 @@ public Object write(VirtualFrame frame, ByteList bytes) {
}

if (Character.isJavaIdentifierStart(currentChar)) {
switch (endianness) {
case LITTLE:
currentByte |= (((currentChar & 15) + 9) & 15) << 4;
break;
case BIG:
currentByte |= ((currentChar & 15) + 9) & 15;
break;
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
currentByte |= (((currentChar & 15) + 9) & 15) << 4;
} else {
currentByte |= ((currentChar & 15) + 9) & 15;
}
} else {
switch (endianness) {
case LITTLE:
currentByte |= (currentChar & 15) << 4;
break;
case BIG:
currentByte |= currentChar & 15;
break;
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
currentByte |= (currentChar & 15) << 4;
} else {
currentByte |= currentChar & 15;
}
}

if (((n - 1) & 1) != 0) {
switch (endianness) {
case LITTLE:
currentByte >>= 4;
break;
case BIG:
currentByte <<= 4;
break;
if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
currentByte >>= 4;
} else {
currentByte <<= 4;
}
} else {
writeByte(frame, (byte) currentByte);
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ public FormatParser(RubyContext context) {
public CallTarget parse(ByteList format) {
final FormatTokenizer tokenizer = new FormatTokenizer(context, format);
final PackNode body = parse(tokenizer);
return Truffle.getRuntime().createCallTarget(new PackRootNode(PackParser.describe(format.toString()), encoding, body));
return Truffle.getRuntime().createCallTarget(new PackRootNode(PackCompiler.describe(format.toString()), encoding, body));
}

public PackNode parse(FormatTokenizer tokenizer) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.parser;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.jruby.truffle.format.nodes.PackRootNode;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

import org.jruby.truffle.format.parser.PackLexer;
import org.jruby.truffle.format.parser.PackParser;

public class PackCompiler {

private final RubyContext context;
private final RubyNode currentNode;

public PackCompiler(RubyContext context, RubyNode currentNode) {
this.context = context;
this.currentNode = currentNode;
}

public CallTarget compile(String format) {
if (format.length() > 32) {
format = recoverLoop(format);
}

final PackErrorListener errorListener = new PackErrorListener(context, currentNode);

final ANTLRInputStream input = new ANTLRInputStream(format.toString());

final PackLexer lexer = new PackLexer(input);
lexer.removeErrorListeners();
lexer.addErrorListener(errorListener);

final CommonTokenStream tokens = new CommonTokenStream(lexer);

final PackParser parser = new PackParser(tokens);

final PackTreeBuilder builder = new PackTreeBuilder(context, currentNode);
parser.addParseListener(builder);

parser.removeErrorListeners();
parser.addErrorListener(errorListener);

parser.sequence();

return Truffle.getRuntime().createCallTarget(
new PackRootNode(describe(format), builder.getEncoding(), builder.getNode()));
}

/**
* Format strings can sometimes be dynamically generated with code such as:
* <p>
* <code>'x' + ('NX' * size)</code>
* <p>
* This is problematic for us as it expands to:
* <p>
* <code>xNXNXNXNXNXNXNXNXNXNX...</code>
* <p>
* We will struggle to compile that with a large value for size because it
* will generate a huge number of nodes. Even if we could compile it, it's
* not great for memory consumption or the instruction cache. Instead, we'd
* like to recover the loop in there and convert it to:
* <p>
* <code>x(NX)1000</code>
* <p>
* We could try and do something really sophisticated here, with nested
* loops and finding the optimal pattern, but for the moment we just look
* for one simple loop.
* <p>
* To do that, for each character we look 1..n characters behind and see if
* that pattern is repeated. If it is we have the loop. Nothing more
* complicated than that.
*/
private String recoverLoop(String format) {
int break_point = 0;

while (break_point < format.length()) {
if ("0123456789*".indexOf(format.charAt(break_point)) != -1) {
break_point++;
continue;
}

int repeated_length = 1;
int max_repeated_length = -1;

while (repeated_length <= break_point && break_point + repeated_length <= format.length()) {
if (format.substring(break_point - repeated_length, break_point)
.equals(format.substring(break_point, break_point + repeated_length))) {
max_repeated_length = repeated_length;
}

repeated_length++;
}

if (max_repeated_length == -1) {
break_point++;
} else {
final String repeated = format.substring(break_point, break_point + max_repeated_length);

int count = 2;
int rep_point = break_point + max_repeated_length;

while (rep_point + max_repeated_length <= format.length()) {
if (!format.substring(rep_point, rep_point + max_repeated_length).equals(repeated)) {
break;
}

count++;
rep_point += max_repeated_length;
}

final StringBuilder builder = new StringBuilder();
builder.append(format.substring(0, break_point - max_repeated_length));
builder.append('(');
builder.append(repeated);
builder.append(')');
builder.append(count);
builder.append(format.substring(rep_point));
format = builder.toString();
}

}

return format;
}

/**
* Provide a simple string describing the format expression that is short
* enough to be used in Truffle and Graal diagnostics.
*/
public static String describe(String format) {
format = format.replace("\\s+", "");

if (format.length() > 10) {
format = format.substring(0, 10) + "…";
}

return format;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.parser;

import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;

public class PackErrorListener extends BaseErrorListener {

private final RubyContext context;
private final RubyNode currentNode;

public PackErrorListener(RubyContext context, RubyNode currentNode) {
this.context = context;
this.currentNode = currentNode;
}

@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
throw new RaiseException(context.getCoreLibrary().argumentError(msg, currentNode));
}

}
576 changes: 0 additions & 576 deletions truffle/src/main/java/org/jruby/truffle/format/parser/PackParser.java

This file was deleted.

This file was deleted.

Large diffs are not rendered by default.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@

import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.format.parser.PackCompiler;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.RubyRootNode;
@@ -46,7 +47,6 @@
import org.jruby.truffle.nodes.methods.DeclarationContext;
import org.jruby.truffle.nodes.objects.*;
import org.jruby.truffle.nodes.yield.YieldDispatchHeadNode;
import org.jruby.truffle.format.parser.PackParser;
import org.jruby.truffle.format.runtime.PackResult;
import org.jruby.truffle.format.runtime.exceptions.*;
import org.jruby.truffle.runtime.NotProvided;
@@ -2479,12 +2479,7 @@ public Object pack(VirtualFrame frame, DynamicObject array, Object format) {
@TruffleBoundary
protected CallTarget compileFormat(DynamicObject format) {
assert RubyGuards.isRubyString(format);
try {
return new PackParser(getContext()).parse(format.toString(), false);
} catch (FormatException e) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().argumentError(e.getMessage(), this));
}
return new PackCompiler(getContext(), this).compile(format.toString());
}

protected int getCacheLimit() {

0 comments on commit ac552f5

Please sign in to comment.