Skip to content

Commit

Permalink
Showing 2 changed files with 161 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -38,12 +38,13 @@
*/
package org.jruby.truffle.stdlib.psych;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jcodings.Encoding;
import org.jcodings.specific.UTF8Encoding;
@@ -68,6 +69,7 @@
import org.jruby.truffle.language.objects.ReadObjectFieldNodeGen;
import org.jruby.truffle.language.objects.TaintNode;
import org.jruby.truffle.language.objects.TaintNodeGen;
import org.jruby.truffle.util.BoundaryUtils.BoundaryIterable;
import org.jruby.util.ByteList;
import org.jruby.util.io.EncodingUtils;
import org.yaml.snakeyaml.DumperOptions;
@@ -76,6 +78,7 @@
import org.yaml.snakeyaml.events.DocumentEndEvent;
import org.yaml.snakeyaml.events.DocumentStartEvent;
import org.yaml.snakeyaml.events.Event;
import org.yaml.snakeyaml.events.Event.ID;
import org.yaml.snakeyaml.events.MappingStartEvent;
import org.yaml.snakeyaml.events.ScalarEvent;
import org.yaml.snakeyaml.events.SequenceStartEvent;
@@ -85,11 +88,9 @@
import org.yaml.snakeyaml.reader.ReaderException;
import org.yaml.snakeyaml.reader.StreamReader;
import org.yaml.snakeyaml.scanner.ScannerException;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;

@CoreClass("Psych::Parser")
@@ -135,36 +136,21 @@ public Object parse(
@Cached("createMethodCall()") CallDispatchHeadNode callEndStreamNode,
@Cached("new()") SnippetNode raiseSyntaxErrorSnippetNode,
@Cached("new()") SnippetNode tagPushNode,
@Cached("createTaintNode()") TaintNode taintNode) {
CompilerDirectives.bailout("Psych parsing cannot be compiled");
@Cached("createTaintNode()") TaintNode taintNode,
@Cached("create()") BranchProfile errorProfile) {

final boolean tainted = (boolean) taintedNode.execute(frame, "yaml.tainted? || yaml.is_a?(IO)", "yaml", yaml);

final StreamReader reader;

if (!RubyGuards.isRubyString(yaml) && respondToReadNode.doesRespondTo(frame, "read", yaml)) {
final Encoding enc = UTF8Encoding.INSTANCE;
final Charset charset = enc.getCharset();
reader = new StreamReader(new InputStreamReader(new InputStreamAdapter(getContext(), yaml), charset));
reader = newStreamReader(yaml);
} else {
ByteList byteList = StringOperations.getByteListReadOnly(toStrNode.executeToStr(frame, yaml));
Encoding encoding = byteList.getEncoding();

if (!(encoding instanceof UnicodeEncoding)) {
byteList = EncodingUtils.strConvEnc(getContext().getJRubyRuntime().getCurrentContext(),
byteList, encoding, UTF8Encoding.INSTANCE);

encoding = UTF8Encoding.INSTANCE;
}

reader = new StreamReader(
new InputStreamReader(
new ByteArrayInputStream(
byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize()),
encoding.getCharset()));
reader = newStringReader(byteList);
}

final Parser parser = new ParserImpl(reader);
final Parser parser = newParser(reader);

try {
if (isNil(path) && respondToPathNode.doesRespondTo(frame, "path", yaml)) {
@@ -174,11 +160,12 @@ public Object parse(
final Object handler = readHandlerNode.execute(parserObject);

while (true) {
Event event = parser.getEvent();
Event event = getParserEvent(parser);

if (event.is(Event.ID.StreamStart)) {
if (isEvent(event, Event.ID.StreamStart)) {
callStartStreamNode.call(frame, handler, "start_stream", YAMLEncoding.YAML_ANY_ENCODING.ordinal());
} else if (event.is(Event.ID.DocumentStart)) {

} else if (isEvent(event, Event.ID.DocumentStart)) {
final DocumentStartEvent startEvent = (DocumentStartEvent) event;

final DumperOptions.Version versionOptions = startEvent.getVersion();
@@ -189,7 +176,7 @@ public Object parse(
if (versionInts == null) {
versionArray = Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), null, 0);
} else {
versionArray = Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), new Object[]{
versionArray = Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), new Object[] {
versionInts[0], versionInts[1]
}, 2);
}
@@ -198,9 +185,9 @@ public Object parse(
DynamicObject tags = Layouts.ARRAY.createArray(coreLibrary().getArrayFactory(), null, 0);

if (tagsMap != null && tagsMap.size() > 0) {
for (Map.Entry<String, String> tag : tagsMap.entrySet()) {
Object key = stringFor(tag.getKey(), tainted, taintNode);
Object value = stringFor(tag.getValue(), tainted, taintNode);
for (Map.Entry<String, String> tag : BoundaryIterable.wrap(tagsMap.entrySet())) {
Object key = stringFor(getKey(tag), tainted, taintNode);
Object value = stringFor(getValue(tag), tainted, taintNode);
tagPushNode.execute(frame,
"tags.push [key, value]",
"tags", tags,
@@ -211,15 +198,18 @@ public Object parse(

Object notExplicit = !startEvent.getExplicit();
callStartDocumentNode.call(frame, handler, "start_document", versionArray, tags, notExplicit);
} else if (event.is(Event.ID.DocumentEnd)) {

} else if (isEvent(event, Event.ID.DocumentEnd)) {
final DocumentEndEvent endEvent = (DocumentEndEvent) event;
Object notExplicit = !endEvent.getExplicit();
callEndDocumentNode.call(frame, handler, "end_document", notExplicit);
} else if (event.is(Event.ID.Alias)) {

} else if (isEvent(event, Event.ID.Alias)) {
final AliasEvent aliasEvent = (AliasEvent) event;
Object alias = stringOrNilFor(aliasEvent.getAnchor(), tainted, taintNode);
callAliasNode.call(frame, handler, "alias", alias);
} else if (event.is(Event.ID.Scalar)) {

} else if (isEvent(event, Event.ID.Scalar)) {
final ScalarEvent scalarEvent = (ScalarEvent) event;

Object anchor = stringOrNilFor(scalarEvent.getAnchor(), tainted, taintNode);
@@ -229,9 +219,9 @@ public Object parse(
Object style = translateStyle(scalarEvent.getStyle());
Object val = stringFor(scalarEvent.getValue(), tainted, taintNode);

callScalarNode.call(frame, handler, "scalar", val, anchor, tag, plain_implicit, quoted_implicit,
style);
} else if (event.is(Event.ID.SequenceStart)) {
callScalarNode.call(frame, handler, "scalar", val, anchor, tag, plain_implicit, quoted_implicit, style);

} else if (isEvent(event, Event.ID.SequenceStart)) {
final SequenceStartEvent sequenceStartEvent = (SequenceStartEvent) event;

Object anchor = stringOrNilFor(sequenceStartEvent.getAnchor(), tainted, taintNode);
@@ -240,9 +230,11 @@ public Object parse(
Object style = translateFlowStyle(sequenceStartEvent.getFlowStyle());

callStartSequenceNode.call(frame, handler, "start_sequence", anchor, tag, implicit, style);
} else if (event.is(Event.ID.SequenceEnd)) {

} else if (isEvent(event, Event.ID.SequenceEnd)) {
callEndSequenceNode.call(frame, handler, "end_sequence");
} else if (event.is(Event.ID.MappingStart)) {

} else if (isEvent(event, Event.ID.MappingStart)) {
final MappingStartEvent mappingStartEvent = (MappingStartEvent) event;

Object anchor = stringOrNilFor(mappingStartEvent.getAnchor(), tainted, taintNode);
@@ -251,14 +243,17 @@ public Object parse(
Object style = translateFlowStyle(mappingStartEvent.getFlowStyle());

callStartMappingNode.call(frame, handler, "start_mapping", anchor, tag, implicit, style);
} else if (event.is(Event.ID.MappingEnd)) {

} else if (isEvent(event, Event.ID.MappingEnd)) {
callEndMappingNode.call(frame, handler, "end_mapping");
} else if (event.is(Event.ID.StreamEnd)) {

} else if (isEvent(event, Event.ID.StreamEnd)) {
callEndStreamNode.call(frame, handler, "end_stream");
break;
}
}
} catch (ParserException pe) {
} catch (ParserException | ScannerException pe) {
errorProfile.enter();
final Mark mark = pe.getProblemMark();

raiseSyntaxErrorSnippetNode.execute(frame,
@@ -267,36 +262,87 @@ public Object parse(
"line", mark.getLine(),
"col", mark.getColumn(),
"offset", mark.getIndex(),
"problem", pe.getProblem() == null ? nil() : createString(new ByteList(pe.getProblem().getBytes(StandardCharsets.UTF_8))),
"context", pe.getContext() == null ? nil() : createString(new ByteList(pe.getContext().getBytes(StandardCharsets.UTF_8))));
} catch (ScannerException se) {
final Mark mark = se.getProblemMark();

raiseSyntaxErrorSnippetNode.execute(frame,
"raise Psych::SyntaxError.new(file, line, col, offset, problem, context)",
"file", path,
"line", mark.getLine(),
"col", mark.getColumn(),
"offset", mark.getIndex(),
"problem", se.getProblem() == null ? nil() : createString(new ByteList(se.getProblem().getBytes(StandardCharsets.UTF_8))),
"context", se.getContext() == null ? nil() : createString(new ByteList(se.getContext().getBytes(StandardCharsets.UTF_8))));
"problem", pe.getProblem() == null ? nil() : createUTF8String(pe.getProblem()),
"context", pe.getContext() == null ? nil() : createUTF8String(pe.getContext()));
} catch (ReaderException re) {
errorProfile.enter();

raiseSyntaxErrorSnippetNode.execute(frame,
"raise Psych::SyntaxError.new(file, line, col, offset, problem, context)",
"file", path,
"line", 0,
"col", 0,
"offset", re.getPosition(),
"problem", re.getName() == null ? nil() : createString(new ByteList(re.getName().getBytes(StandardCharsets.UTF_8))),
"context", re.toString() == null ? nil() : createString(new ByteList(re.toString().getBytes(StandardCharsets.UTF_8))));
"problem", re.getName() == null ? nil() : createUTF8String(re.getName()),
"context", toString(re) == null ? nil() : createUTF8String(toString(re)));
} catch (Throwable t) {
errorProfile.enter();
Helpers.throwException(t);
return parserObject;
}

return parserObject;
}

@TruffleBoundary
private StreamReader newStreamReader(DynamicObject yaml) {
final Encoding enc = UTF8Encoding.INSTANCE;
final Charset charset = enc.getCharset();
return new StreamReader(new InputStreamReader(new InputStreamAdapter(getContext(), yaml), charset));
}

@TruffleBoundary
private StreamReader newStringReader(ByteList byteList) {
Encoding encoding = byteList.getEncoding();

if (!(encoding instanceof UnicodeEncoding)) {
byteList = EncodingUtils.strConvEnc(getContext().getJRubyRuntime().getCurrentContext(),
byteList, encoding, UTF8Encoding.INSTANCE);

encoding = UTF8Encoding.INSTANCE;
}

return new StreamReader(
new InputStreamReader(
new ByteArrayInputStream(
byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize()),
encoding.getCharset()));
}

@TruffleBoundary
private ParserImpl newParser(StreamReader reader) {
return new ParserImpl(reader);
}

@TruffleBoundary
private Event getParserEvent(Parser parser) {
return parser.getEvent();
}

@TruffleBoundary
private boolean isEvent(Event event, ID id) {
return event.is(id);
}

private DynamicObject createUTF8String(String value) {
return createString(StringOperations.encodeRope(value, UTF8Encoding.INSTANCE));
}

@TruffleBoundary
private String getKey(Map.Entry<String, String> tag) {
return tag.getKey();
}

@TruffleBoundary
private String getValue(Map.Entry<String, String> tag) {
return tag.getValue();
}

@TruffleBoundary
private String toString(ReaderException re) {
return re.toString();
}

protected ReadObjectFieldNode createReadHandlerNode() {
return ReadObjectFieldNodeGen.create("@handler", nil());
}
@@ -341,6 +387,7 @@ private static int translateFlowStyle(Boolean flowStyle) {
}
}

@TruffleBoundary
private Object stringOrNilFor(String value, boolean tainted, TaintNode taintNode) {
if (value == null) {
return nil();
@@ -349,6 +396,7 @@ private Object stringOrNilFor(String value, boolean tainted, TaintNode taintNode
}
}

@TruffleBoundary
private Object stringFor(String value, boolean tainted, TaintNode taintNode) {
Encoding encoding = getContext().getJRubyRuntime().getDefaultInternalEncoding();

57 changes: 57 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/util/BoundaryUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2016 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.util;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import java.util.Iterator;

public class BoundaryUtils {

public static class BoundaryIterable<E> implements Iterable<E> {

public static <E> Iterable<E> wrap(Iterable<E> iterable) {
return new BoundaryIterable<>(iterable);
}

private final Iterable<E> iterable;

public BoundaryIterable(Iterable<E> iterable) {
this.iterable = iterable;
}

@Override
public Iterator<E> iterator() {
return new BoundaryIterator<E>(iterable.iterator());
}
}

public static class BoundaryIterator<E> implements Iterator<E> {

private final Iterator<E> iterator;

public BoundaryIterator(Iterator<E> iterator) {
this.iterator = iterator;
}

@TruffleBoundary
@Override
public boolean hasNext() {
return iterator.hasNext();
}

@TruffleBoundary
@Override
public E next() {
return iterator.next();
}

}

}

0 comments on commit 29081b7

Please sign in to comment.