Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into truffle-om-dsl-jars
Browse files Browse the repository at this point in the history
chrisseaton committed Sep 22, 2015
2 parents e06bdf6 + 935c331 commit 6948f92
Showing 30 changed files with 326 additions and 599 deletions.
25 changes: 20 additions & 5 deletions core/src/main/java/org/jruby/ast/SymbolNode.java
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
package org.jruby.ast;

import java.awt.image.ByteLookupTable;
import java.nio.charset.Charset;
import java.util.List;

import org.jcodings.Encoding;
@@ -48,6 +49,7 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

/**
* Represents a symbol (:symbol_name).
@@ -56,14 +58,27 @@ public class SymbolNode extends Node implements ILiteralNode, INameNode {
private String name;
private Encoding encoding;

public SymbolNode(ISourcePosition position, ByteList value) {
// Interned ident path (e.g. [':', ident]).
public SymbolNode(ISourcePosition position, String name, Encoding encoding, int cr) {
super(position, false);
this.name = value.toString().intern();
// FIXME: A full scan to determine whether we should back off to US-ASCII. Lexer should just do this properly.
if (value.lengthEnc() == value.length()) {
this.name = name; // Assumed all names are already intern'd by lexer.

if (encoding == USASCIIEncoding.INSTANCE || cr == StringSupport.CR_7BIT) {
this.encoding = USASCIIEncoding.INSTANCE;
} else {
this.encoding = value.getEncoding();
this.encoding = encoding;
}
}

// String path (e.g. [':', str_beg, str_content, str_end])
public SymbolNode(ISourcePosition position, ByteList value) {
super(position, false);
this.name = value.toString().intern();

if (value.getEncoding() != USASCIIEncoding.INSTANCE) {
int size = value.realSize();
this.encoding = value.getEncoding().strLength(value.unsafeBytes(), value.begin(), size) == size ?
USASCIIEncoding.INSTANCE : value.getEncoding();
}
}

14 changes: 13 additions & 1 deletion core/src/main/java/org/jruby/lexer/yacc/RubyLexer.java
Original file line number Diff line number Diff line change
@@ -286,9 +286,16 @@ public static Keyword getKeyword(String str) {
private LexState lex_state;
private LexState last_state;
public ISourcePosition tokline;
private int tokenCR;

public int getTokenCR() {
return tokenCR;
}

public void newtok(boolean unreadOnce) {
tokline = getPosition();
// We assume all idents are 7BIT until they aren't.
tokenCR = StringSupport.CR_7BIT;

tokp = lex_p - (unreadOnce ? 1 : 0); // We use tokp of ripper to mark beginning of tokens.
}
@@ -2706,7 +2713,12 @@ public void readUTFEscapeRegexpLiteral(ByteList buffer) throws IOException {
public boolean tokadd_mbchar(int first_byte) {
int length = precise_mbclen();

if (length <= 0) compile_error("invalid multibyte char (" + current_enc + ")");

if (length <= 0) {
compile_error("invalid multibyte char (" + current_enc + ")");
} else if (length > 1) {
tokenCR = StringSupport.CR_VALID;
}

lex_p += length - 1; // we already read first byte so advance pointer for remainder

11 changes: 1 addition & 10 deletions core/src/main/java/org/jruby/parser/ParserSupport.java
Original file line number Diff line number Diff line change
@@ -882,16 +882,7 @@ public DStrNode createDStrNode(ISourcePosition position) {
}

public Node asSymbol(ISourcePosition position, String value) {
// FIXME: tLABEL and identifiers could return ByteList and not String and make String on-demand for method names
// or lvars. This would prevent this re-extraction of bytes from a string with proper charset
try {
Charset charset = lexer.getEncoding().getCharset();
if (charset != null) return new SymbolNode(position, new ByteList(value.getBytes(charset), lexer.getEncoding()));
} catch (UnsupportedCharsetException e) {}

// for non-charsets we are screwed here since bytes will file.encoding and not what we read them as (see above FIXME for
// a much more invasive solution.
return new SymbolNode(position, new ByteList(value.getBytes(), lexer.getEncoding()));
return new SymbolNode(position, value, lexer.getEncoding(), lexer.getTokenCR());
}

public Node asSymbol(ISourcePosition position, Node value) {
16 changes: 8 additions & 8 deletions core/src/main/java/org/jruby/util/URLResource.java
Original file line number Diff line number Diff line change
@@ -153,7 +153,7 @@ public Channel openChannel( ModeFlags flags, int perm ) throws ResourceException
return Channels.newChannel(inputStream());
}

public static FileResource createClassloaderURI(Ruby runtime, String pathname, boolean isFile) {
public static FileResource createClassloaderURI(Ruby runtime, String pathname, boolean asFile) {
ClassLoader cl = runtime != null ? runtime.getJRubyClassLoader() : RubyInstanceConfig.defaultClassLoader();
try
{
@@ -163,7 +163,7 @@ public static FileResource createClassloaderURI(Ruby runtime, String pathname, b
}
final URL url = cl.getResource(pathname);
String[] files = null;
if (!isFile) {
if (!asFile) {
files = listClassLoaderFiles(cl, pathname);
if (files == null) {
// no .jrubydir found
@@ -238,19 +238,19 @@ private static boolean addDirectoryEntries(Set<String> entries, URL url,
return isDirectory;
}

public static FileResource create(Ruby runtime, String pathname, boolean isFile) {
public static FileResource create(Ruby runtime, String pathname, boolean asFile) {
if (!pathname.startsWith(URI)) {
return null;
}
// GH-2005 needs the replace
pathname = pathname.substring(URI.length()).replace("\\", "/");
if (pathname.startsWith(CLASSLOADER)) {
return createClassloaderURI(runtime, pathname.substring(CLASSLOADER.length()), isFile);
return createClassloaderURI(runtime, pathname.substring(CLASSLOADER.length()), asFile);
}
return createRegularURI(pathname, isFile);
return createRegularURI(pathname, asFile);
}

private static FileResource createRegularURI(String pathname, boolean isFile) {
private static FileResource createRegularURI(String pathname, boolean asFile) {
URL url;
try
{
@@ -270,7 +270,7 @@ private static FileResource createRegularURI(String pathname, boolean isFile) {
// file does not exists
return new URLResource(URI + pathname, (URL)null, null);
}
String[] files = isFile ? null : listFiles(pathname);
String[] files = asFile ? null : listFiles(pathname);
if (files != null) {
return new URLResource(URI + pathname, (URL)null, files);
}
@@ -324,7 +324,7 @@ private static String[] listFilesFromInputStream(InputStream is) {
private static String[] listClassLoaderFiles(ClassLoader classloader, String pathname) {
try
{
String path = pathname + (pathname.equals("") ? ".jrubydir" : "/.jrubydir");
String path = pathname + (pathname.equals("") || pathname.endsWith("/") ? ".jrubydir" : "/.jrubydir");
Enumeration<URL> urls = classloader.getResources(path);
if (!urls.hasMoreElements()) {
return null;
23 changes: 20 additions & 3 deletions core/src/main/java/org/jruby/util/io/SelectorPool.java
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@

import java.io.IOException;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.List;

import java.nio.channels.spi.SelectorProvider;
@@ -52,6 +53,7 @@
*/
public class SelectorPool {
private final Map<SelectorProvider, List<Selector>> pool = new HashMap<SelectorProvider, List<Selector>>();
private final List<Selector> openSelectors = new ArrayList<Selector>();

/**
* Get a selector from the pool (or create a new one). Selectors come from
@@ -87,7 +89,8 @@ public synchronized void put(Selector selector) {
/**
* Clean up a pool.
*
* All selectors in a pool are closed and the pool gets empty.
* All selectors in a pool or handed out from the pool are closed
* and the pool gets emptied.
*
*/
public synchronized void cleanup() {
@@ -103,18 +106,32 @@ public synchronized void cleanup() {
}
}
pool.clear();

for (Selector selector : openSelectors) {
try {
selector.close();
} catch (IOException ioe) {
// ignore IOException at termination.
}
}
openSelectors.clear();
}

private Selector retrieveFromPool(SelectorProvider provider) throws IOException {
List<Selector> providerPool = pool.get(provider);
Selector selector;
if (providerPool != null && !providerPool.isEmpty()) {
return providerPool.remove(providerPool.size() - 1);
selector = providerPool.remove(providerPool.size() - 1);
} else {
selector = SelectorFactory.openWithRetryFrom(null, provider);
}

return SelectorFactory.openWithRetryFrom(null, provider);
openSelectors.add(selector);
return selector;
}

private void returnToPool(Selector selector) {
openSelectors.remove(selector);
if (selector.isOpen()) {
SelectorProvider provider = selector.provider();
List<Selector> providerPool = pool.get(provider);
37 changes: 31 additions & 6 deletions core/src/test/java/org/jruby/util/URLResourceTest.java
Original file line number Diff line number Diff line change
@@ -17,7 +17,19 @@ public void testDirectory(){
assertTrue(resource.isDirectory());
assertTrue(resource.exists());
assertEquals(Arrays.asList(resource.list()),
Arrays.asList(new String[] {".", "dir_without_listing", "dir_with_listing"}));
Arrays.asList(new String[] {".", "dir_without_listing", "dir_with_listing"}));
}

public void testDirectoryWithTrailingSlash(){
String uri = Thread.currentThread().getContextClassLoader().getResource( "somedir" ).toExternalForm();
FileResource resource = URLResource.create((Ruby) null, "uri:" + uri + "/", false);

assertNotNull(resource );
assertFalse(resource.isFile());
assertTrue(resource.isDirectory());
assertTrue(resource.exists());
assertEquals(Arrays.asList(resource.list()),
Arrays.asList(new String[] {".", "dir_without_listing", "dir_with_listing"}));
}

public void testNoneDirectory(){
@@ -53,18 +65,31 @@ public void testNonExistingFile(){
assertNull(resource.list());
}

public void testDirectoryClassloader()
{
public void testDirectoryClassloader() {
FileResource resource = URLResource.create((Ruby) null,
"uri:classloader:/somedir", false);

assertNotNull(resource);
assertFalse(resource.isFile());
assertTrue(resource.isDirectory());
assertTrue(resource.exists());
assertEquals(Arrays.asList(resource.list()),
Arrays.asList(new String[]{".", "dir_without_listing",
"dir_with_listing"}));
}

public void testDirectoryWithTrailingClassloader()
{
FileResource resource = URLResource.create((Ruby) null,
"uri:classloader:/somedir/", false);

assertNotNull( resource );
assertFalse( resource.isFile() );
assertTrue( resource.isDirectory() );
assertTrue( resource.exists() );
assertEquals( Arrays.asList( resource.list() ),
Arrays.asList( new String[] { ".", "dir_without_listing",
"dir_with_listing" } ) );
Arrays.asList( new String[] { ".", "dir_without_listing",
"dir_with_listing" } ) );
}

public void testNoneDirectoryClassloader()
@@ -83,7 +108,7 @@ public void testNoneDirectoryClassloader()
public void testFileClassloader()
{
FileResource resource = URLResource.create((Ruby) null,
"uri:classloader:/somedir/.jrubydir", false );
"uri:classloader:/somedir/.jrubydir", true );

assertNotNull( resource );
assertTrue( resource.isFile() );
2 changes: 1 addition & 1 deletion lib/ruby/stdlib/io/console.rb
Original file line number Diff line number Diff line change
@@ -156,7 +156,7 @@ def self.console(sym = nil)
con = @console
end

if !con.kind_of?(File) || !con.open? || !con.readable? # MRI checks IO internals here
if !con.kind_of?(File) || (con.kind_of?(IO) && !con.open? || !con.readable?) # MRI checks IO internals here
remove_instance_variable :@console if defined?(@console)
con = nil
end
22 changes: 22 additions & 0 deletions maven/jruby-complete/src/it/integrity/pom.xml
Original file line number Diff line number Diff line change
@@ -233,6 +233,28 @@
</arguments>
</configuration>
</execution>
<execution>
<id>META-INF/jruby.home is not a file - GH-3342</id>
<phase>test</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<workingDirectory>${basedir}</workingDirectory>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<classpath/>
<argument>org.jruby.Main</argument>
<argument>-e</argument>
<argument>
puts "jruby home is a file: " +
File.file?("uri:classloader:/META-INF/jruby.home").to_s +
File.file?("uri:classloader:/META-INF/jruby.home/").to_s
</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
3 changes: 3 additions & 0 deletions maven/jruby-complete/src/it/integrity/verify.bsh
Original file line number Diff line number Diff line change
@@ -61,3 +61,6 @@ if ( !file.exists() )

expected = "uri:classloader://META-INF/jruby.home";
if ( !log.contains( expected ) ) throw new RuntimeException( "log file does not contain '" + expected + "'" );

expected = "jruby home is a file: falsefalse";
if ( !log.contains( expected ) ) throw new RuntimeException( "log file does not contain '" + expected + "'" );
40 changes: 34 additions & 6 deletions spec/regression/embedded_runtime_leak_spec.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,48 @@
describe "embedded runtimes" do
it "should not leak runtimes after tearing them down" do
num_runtimes = 10
create_runtimes(10) do |runtime|
# nothing to do actually - the runtimes will by default build up
# and OOM if they aren't being garbage collected
end
end

it "should not leak runtimes blocked on IO" do
create_runtimes(10) do |runtime|
latch = java.util.concurrent.CountDownLatch.new(1)
Thread.new do
latch.countDown
# we'll get an OOM if the blocking TCPServer#accept call below
# doesn't get interrupted during runtime teardown
runtime.evalScriptlet <<-EOS
require "socket"
server = TCPServer.new(0)
begin
server.accept
rescue Exception
end
EOS
end
latch.await # spawned thread has started
sleep 0.5 # give time for the new runtime to block on accept
end
end

def create_runtimes(num_runtimes)
mbean = java.lang.management.ManagementFactory.getMemoryMXBean
lambda do
expect do
num_runtimes.times do
instance = org.jruby.Ruby.newInstance
instance.evalScriptlet <<-EOS
runtime = org.jruby.Ruby.newInstance
runtime.evalScriptlet <<-EOS
# eat up some memory in each runtime
$arr = 500000.times.map { |i| "foobarbaz\#{i}" }
EOS
instance.tearDown(false)
yield runtime if block_given?
runtime.tearDown(false)
# Make sure GC can keep up
while mbean.getObjectPendingFinalizationCount > 0
sleep 0.2
end
end
end.should_not raise_error
end.not_to raise_error
end
end
11 changes: 9 additions & 2 deletions test/truffle/memory/minimum-heap.rb
Original file line number Diff line number Diff line change
@@ -20,11 +20,18 @@
TOLERANCE = 1024
UPPER_FACTOR = 4

begin
`timeout --help`
TIMEOUT = 'timeout'
rescue
TIMEOUT = 'gtimeout'
end

def can_run(heap)
print "trying #{heap} KB... "

output = `#{COMMAND} -J-Xmx#{heap}k #{OPTIONS} 2>&1`
can_run = !output.include?('OutOfMemoryError')
output = `#{TIMEOUT} 120s #{COMMAND} -J-Xmx#{heap}k #{OPTIONS} 2>&1`
can_run = $?.exitstatus != 124 && !output.include?('OutOfMemoryError')

if can_run
puts "yes"
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@
import org.jruby.truffle.nodes.methods.LookupMethodNodeGen;
import org.jruby.truffle.nodes.objects.*;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNodeGen;
import org.jruby.truffle.nodes.rubinius.ObjectPrimitiveNodes;
import org.jruby.truffle.nodes.rubinius.ObjectPrimitiveNodesFactory;
import org.jruby.truffle.pack.parser.FormatParser;
@@ -2116,7 +2117,7 @@ public UntaintNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
isFrozenNode = IsFrozenNodeGen.create(context, sourceSection, null);
isTaintedNode = IsTaintedNodeGen.create(context, sourceSection, null);
writeTaintNode = new WriteHeadObjectFieldNode(Layouts.TAINTED_IDENTIFIER);
writeTaintNode = WriteHeadObjectFieldNodeGen.create(Layouts.TAINTED_IDENTIFIER);
}

@Specialization
Original file line number Diff line number Diff line change
@@ -204,10 +204,9 @@ public GetIndexNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object getIndex(DynamicObject matchData, int index, NotProvided length) {
CompilerDirectives.transferToInterpreter();

final Object[] values = Arrays.copyOf(Layouts.MATCH_DATA.getValues(matchData), Layouts.MATCH_DATA.getValues(matchData).length);
final int normalizedIndex = ArrayOperations.normalizeIndex(values.length, index);

@@ -218,20 +217,19 @@ public Object getIndex(DynamicObject matchData, int index, NotProvided length) {
}
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object getIndex(DynamicObject matchData, int index, int length) {
CompilerDirectives.transferToInterpreter();
// TODO BJF 15-May-2015 Need to handle negative indexes and lengths and out of bounds
final Object[] values = Arrays.copyOf(Layouts.MATCH_DATA.getValues(matchData), Layouts.MATCH_DATA.getValues(matchData).length);
final int normalizedIndex = ArrayOperations.normalizeIndex(values.length, index);
final Object[] store = Arrays.copyOfRange(values, normalizedIndex, normalizedIndex + length);
return Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), store, length);
}

@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubySymbol(index)")
public Object getIndexSymbol(DynamicObject matchData, DynamicObject index, NotProvided length) {
CompilerDirectives.transferToInterpreter();

try {
ByteList value = Layouts.SYMBOL.getByteList(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getUnsafeBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));
@@ -245,10 +243,9 @@ public Object getIndexSymbol(DynamicObject matchData, DynamicObject index, NotPr
}
}

@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isRubyString(index)")
public Object getIndexString(DynamicObject matchData, DynamicObject index, NotProvided length) {
CompilerDirectives.transferToInterpreter();

try {
ByteList value = Layouts.STRING.getByteList(index);
final int i = Layouts.REGEXP.getRegex(Layouts.MATCH_DATA.getRegexp(matchData)).nameToBackrefNumber(value.getUnsafeBytes(), value.getBegin(), value.getBegin() + value.getRealSize(), Layouts.MATCH_DATA.getRegion(matchData));
@@ -275,6 +272,7 @@ public Object getIndex(VirtualFrame frame, DynamicObject matchData, Object index
return getIndex(matchData, toIntNode.doInt(frame, index), NotProvided.INSTANCE);
}

@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isIntegerFixnumRange(range)")
public Object getIndex(DynamicObject matchData, DynamicObject range, NotProvided len) {
final Object[] values = Arrays.copyOf(Layouts.MATCH_DATA.getValues(matchData), Layouts.MATCH_DATA.getValues(matchData).length);
@@ -298,13 +296,10 @@ public BeginNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object begin(DynamicObject matchData, int index) {
CompilerDirectives.transferToInterpreter();

if (badIndexProfile.profile((index < 0) || (index >= Layouts.MATCH_DATA.getRegion(matchData).numRegs))) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(
getContext().getCoreLibrary().indexError(String.format("index %d out of matches", index), this));

@@ -322,10 +317,9 @@ public CapturesNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public DynamicObject toA(DynamicObject matchData) {
CompilerDirectives.transferToInterpreter();

Object[] objects = getCaptures(matchData);
return Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), objects, objects.length);
}
@@ -340,13 +334,10 @@ public EndNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object end(DynamicObject matchData, int index) {
CompilerDirectives.transferToInterpreter();

if (badIndexProfile.profile((index < 0) || (index >= Layouts.MATCH_DATA.getRegion(matchData).numRegs))) {
CompilerDirectives.transferToInterpreter();

throw new RaiseException(
getContext().getCoreLibrary().indexError(String.format("index %d out of matches", index), this));

@@ -395,6 +386,7 @@ public LengthNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public int length(DynamicObject matchData) {
return Arrays.copyOf(Layouts.MATCH_DATA.getValues(matchData), Layouts.MATCH_DATA.getValues(matchData).length).length;
@@ -412,6 +404,7 @@ public PreMatchNode(RubyContext context, SourceSection sourceSection) {
taintResultNode = new TaintResultNode(getContext(), getSourceSection());
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object preMatch(DynamicObject matchData) {
return taintResultNode.maybeTaint(Layouts.MATCH_DATA.getSource(matchData), Layouts.MATCH_DATA.getPre(matchData));
@@ -429,6 +422,7 @@ public PostMatchNode(RubyContext context, SourceSection sourceSection) {
taintResultNode = new TaintResultNode(getContext(), getSourceSection());
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object postMatch(DynamicObject matchData) {
return taintResultNode.maybeTaint(Layouts.MATCH_DATA.getSource(matchData), Layouts.MATCH_DATA.getPost(matchData));
@@ -443,10 +437,9 @@ public ToANode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public DynamicObject toA(DynamicObject matchData) {
CompilerDirectives.transferToInterpreter();

Object[] objects = Arrays.copyOf(Layouts.MATCH_DATA.getValues(matchData), Layouts.MATCH_DATA.getValues(matchData).length);
return Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), objects, objects.length);
}
@@ -459,10 +452,9 @@ public ToSNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public DynamicObject toS(DynamicObject matchData) {
CompilerDirectives.transferToInterpreter();

final ByteList bytes = Layouts.STRING.getByteList(Layouts.MATCH_DATA.getGlobal(matchData)).dup();
return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), bytes, StringSupport.CR_UNKNOWN, null);
}
@@ -475,6 +467,7 @@ public RegexpNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public DynamicObject regexp(DynamicObject matchData) {
return Layouts.MATCH_DATA.getRegexp(matchData);
@@ -489,6 +482,7 @@ public RubiniusSourceNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public DynamicObject rubiniusSource(DynamicObject matchData) {
return Layouts.MATCH_DATA.getSource(matchData);
Original file line number Diff line number Diff line change
@@ -15,8 +15,10 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;

@@ -70,7 +72,7 @@ public Object freezeSymbol(DynamicObject symbol) {
public Object freeze(DynamicObject object) {
if (writeFrozenNode == null) {
CompilerDirectives.transferToInterpreter();
writeFrozenNode = insert(new WriteHeadObjectFieldNode(Layouts.FROZEN_IDENTIFIER));
writeFrozenNode = insert(WriteHeadObjectFieldNodeGen.create(Layouts.FROZEN_IDENTIFIER));
}

writeFrozenNode.execute(object, true);
Original file line number Diff line number Diff line change
@@ -14,8 +14,10 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.layouts.Layouts;
@@ -60,7 +62,7 @@ public Object taintSymbol(DynamicObject symbol) {
public Object taint(DynamicObject object) {
if (writeTaintNode == null) {
CompilerDirectives.transferToInterpreter();
writeTaintNode = insert(new WriteHeadObjectFieldNode(Layouts.TAINTED_IDENTIFIER));
writeTaintNode = insert(WriteHeadObjectFieldNodeGen.create(Layouts.TAINTED_IDENTIFIER));
}
writeTaintNode.execute(object, true);
return object;
Original file line number Diff line number Diff line change
@@ -14,10 +14,12 @@
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

import org.jcodings.specific.UTF8Encoding;
import org.jruby.RubyString;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.layouts.Layouts;
@@ -35,7 +37,7 @@ public WriteInstanceVariableNode(RubyContext context, SourceSection sourceSectio
super(context, sourceSection);
this.receiver = receiver;
this.rhs = rhs;
writeNode = new WriteHeadObjectFieldNode(name);
writeNode = WriteHeadObjectFieldNodeGen.create(name);
this.isGlobal = isGlobal;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -16,7 +16,9 @@
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.*;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.Options;
import org.jruby.truffle.runtime.RubyContext;

@NodeChild("receiver")
@@ -141,7 +143,7 @@ protected DoubleLocation getDoubleLocation(Shape shape) {
}

protected int getCacheLimit() {
return getContext().getOptions().FIELD_LOOKUP_CACHE;
return Options.FIELD_LOOKUP_CACHE;
}

}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -9,37 +9,152 @@
*/
package org.jruby.truffle.nodes.objectstorage;

import java.util.EnumSet;

import org.jruby.truffle.runtime.Options;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.FinalLocationException;
import com.oracle.truffle.api.object.IncompatibleLocationException;
import com.oracle.truffle.api.object.Location;
import com.oracle.truffle.api.object.LocationModifier;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;

public class WriteHeadObjectFieldNode extends Node {
public abstract class WriteHeadObjectFieldNode extends Node {

private final Object name;
@Child private WriteObjectFieldNode first;

public WriteHeadObjectFieldNode(Object name) {
this.name = name;
first = new UninitializedWriteObjectFieldNode(name);
}

public void execute(DynamicObject object, int value) {
first.execute(object, value);
public Object getName() {
return name;
}

public abstract void execute(DynamicObject object, Object value);

@Specialization(
guards = {
"location != null",
"object.getShape() == cachedShape",
"location.canSet(object, value)"
},
assumptions = { "newArray(cachedShape.getValidAssumption(), validLocation)" },
limit = "getCacheLimit()")
public void writeExistingField(DynamicObject object, Object value,
@Cached("object.getShape()") Shape cachedShape,
@Cached("getLocation(object, value)") Location location,
@Cached("createAssumption()") Assumption validLocation) {
try {
location.set(object, value, cachedShape);
} catch (IncompatibleLocationException e) {
// remove this entry
validLocation.invalidate();
execute(object, value);
} catch (FinalLocationException e) {
throw new UnsupportedOperationException("write to final location?", e);
}
}

public void execute(DynamicObject object, long value) {
first.execute(object, value);
@Specialization(
guards = {
"!hasField",
"newShape != null", // workaround for DSL bug
"object.getShape() == oldShape",
"location.canSet(object, value)" },
assumptions = { "newArray(oldShape.getValidAssumption(), newShape.getValidAssumption(), validLocation)" },
limit = "getCacheLimit()")
public void writeNewField(DynamicObject object, Object value,
@Cached("hasField(object, value)") boolean hasField,
@Cached("object.getShape()") Shape oldShape,
@Cached("transitionWithNewField(oldShape, value)") Shape newShape,
@Cached("getNewLocation(newShape)") Location location,
@Cached("createAssumption()") Assumption validLocation) {
try {
location.set(object, value, oldShape, newShape);
} catch (IncompatibleLocationException e) {
// remove this entry
validLocation.invalidate();
execute(object, value);
}
}

public void execute(DynamicObject object, double value) {
first.execute(object, value);
@TruffleBoundary
@Specialization
public void writeUncached(DynamicObject object, Object value) {
object.updateShape();
final Shape shape = object.getShape();
final Property property = shape.getProperty(name);

if (property == null) {
object.define(name, value, 0);
} else {
property.setGeneric(object, value, shape);
}
}

public void execute(DynamicObject object, Object value) {
first.execute(object, value);
protected Location getLocation(DynamicObject object, Object value) {
object.updateShape();
final Shape oldShape = object.getShape();
final Property property = oldShape.getProperty(name);

if (property != null && property.getLocation().canSet(object, value)) {
return property.getLocation();
} else {
return null;
}
}

public Object getName() {
return name;
protected boolean hasField(DynamicObject object, Object value) {
return getLocation(object, value) != null;
}

protected Shape transitionWithNewField(Shape oldShape, Object value) {
// This duplicates quite a bit of DynamicObject.define(), but should be fixed in Truffle soon.
final Property oldProperty = oldShape.getProperty(name);
if (oldProperty != null) {
if (oldProperty.getFlags() == 0 && oldProperty.getLocation().canSet(null, value)) {
return oldShape; // already the right shape
} else {
DynamicObject copy = oldShape.getLayout().newInstance(oldShape);
copy.define(name, value, 0);
return copy.getShape();
}
} else {
final Location location = oldShape.allocator().locationForValue(value, EnumSet.of(LocationModifier.NonNull));
final Property newProperty = Property.create(name, location, 0);
return oldShape.addProperty(newProperty);
}
}

protected Location getNewLocation(Shape newShape) {
return newShape.getProperty(name).getLocation();
}

protected Assumption createAssumption() {
return Truffle.getRuntime().createAssumption();
}

protected int getCacheLimit() {
return Options.FIELD_LOOKUP_CACHE;
}

// workaround for DSL bug
protected Assumption[] newArray(Assumption a1, Assumption a2, Assumption a3) {
return new Assumption[] { a1, a2, a3 };
}

// workaround for DSL bug
protected Assumption[] newArray(Assumption a1, Assumption a2) {
return new Assumption[] { a1, a2 };
}

}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNodeGen;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.object.ObjectIDOperations;
@@ -107,7 +108,7 @@ protected ReadHeadObjectFieldNode createReadObjectIDNode() {
}

protected WriteHeadObjectFieldNode createWriteObjectIDNode() {
return new WriteHeadObjectFieldNode(Layouts.OBJECT_ID_IDENTIFIER);
return WriteHeadObjectFieldNodeGen.create(Layouts.OBJECT_ID_IDENTIFIER);
}

}
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNodeGen;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNodeGen;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
@@ -164,7 +165,7 @@ public static abstract class StatStatPrimitiveNode extends RubiniusPrimitiveNode

public StatStatPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeStatNode = new WriteHeadObjectFieldNode(STAT_IDENTIFIER);
writeStatNode = WriteHeadObjectFieldNodeGen.create(STAT_IDENTIFIER);
}

@TruffleBoundary
@@ -196,7 +197,7 @@ public static abstract class StatFStatPrimitiveNode extends RubiniusPrimitiveNod

public StatFStatPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeStatNode = new WriteHeadObjectFieldNode(STAT_IDENTIFIER);
writeStatNode = WriteHeadObjectFieldNodeGen.create(STAT_IDENTIFIER);
}

@TruffleBoundary
@@ -221,7 +222,7 @@ public static abstract class StatLStatPrimitiveNode extends RubiniusPrimitiveNod

public StatLStatPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeStatNode = new WriteHeadObjectFieldNode(STAT_IDENTIFIER);
writeStatNode = WriteHeadObjectFieldNodeGen.create(STAT_IDENTIFIER);
}

@TruffleBoundary
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ public class Options {
public final int IS_A_CACHE = org.jruby.util.cli.Options.TRUFFLE_IS_A_CACHE.load();
public final int BIND_CACHE = org.jruby.util.cli.Options.TRUFFLE_BIND_CACHE.load();
public final int CONSTANT_LOOKUP_CACHE = org.jruby.util.cli.Options.TRUFFLE_CONSTANT_LOOKUP_CACHE.load();
public final int FIELD_LOOKUP_CACHE = org.jruby.util.cli.Options.TRUFFLE_FIELD_LOOKUP_CACHE.load();
public static final int FIELD_LOOKUP_CACHE = org.jruby.util.cli.Options.TRUFFLE_FIELD_LOOKUP_CACHE.load();
public final int BINDING_LOCAL_VARIABLE_CACHE = org.jruby.util.cli.Options.TRUFFLE_BINDING_LOCAL_VARIABLE_CACHE.load();
public final int SYMBOL_TO_PROC_CACHE = org.jruby.util.cli.Options.TRUFFLE_SYMBOL_TO_PROC_CACHE.load();
public final int ALLOCATE_CLASS_CACHE = org.jruby.util.cli.Options.TRUFFLE_ALLOCATE_CLASS_CACHE.load();

0 comments on commit 6948f92

Please sign in to comment.