8242934: test/jdk/jdk/jfr/tool/TestPrintJSON.java uses nashorn script engine
Reviewed-by: mgronlun
This commit is contained in:
parent
707462edc2
commit
ca53ee2593
@ -948,11 +948,6 @@ javax/script/Test7.java 8239361 generic-
|
||||
jdk/jfr/event/runtime/TestNetworkUtilizationEvent.java 8228990,8229370 generic-all
|
||||
jdk/jfr/event/compiler/TestCodeSweeper.java 8225209 generic-all
|
||||
|
||||
jdk/jfr/api/consumer/TestHiddenMethod.java 8242933 generic-all
|
||||
jdk/jfr/tool/TestPrintJSON.java 8242934 generic-all
|
||||
|
||||
|
||||
|
||||
############################################################################
|
||||
|
||||
# jdk_internal
|
||||
|
551
test/jdk/jdk/jfr/tool/JSONValue.java
Normal file
551
test/jdk/jdk/jfr/tool/JSONValue.java
Normal file
@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jfr.tool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface JSONValue {
|
||||
|
||||
public final class JSONObject implements JSONValue {
|
||||
private final Map<String, JSONValue> value;
|
||||
|
||||
public JSONObject(Map<String, JSONValue> value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject asObject() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONValue get(String k) {
|
||||
return value.get(k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return value.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var builder = new StringBuilder();
|
||||
builder.append("{");
|
||||
for (var key : value.keySet()) {
|
||||
builder.append("\"");
|
||||
builder.append(key);
|
||||
builder.append("\":");
|
||||
builder.append(value.get(key).toString());
|
||||
builder.append(",");
|
||||
}
|
||||
|
||||
var end = builder.length() - 1;
|
||||
if (builder.charAt(end) == ',') {
|
||||
builder.deleteCharAt(end);
|
||||
}
|
||||
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public final class JSONString implements JSONValue {
|
||||
private final String value;
|
||||
|
||||
public JSONString(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
var builder = new StringBuilder();
|
||||
builder.append("\"");
|
||||
|
||||
for (var i = 0; i < value.length(); i++) {
|
||||
var c = value.charAt(i);
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
builder.append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
builder.append("\\\\");
|
||||
break;
|
||||
case '/':
|
||||
builder.append("\\/");
|
||||
break;
|
||||
case '\b':
|
||||
builder.append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
builder.append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
builder.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
builder.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
builder.append("\\t");
|
||||
break;
|
||||
default:
|
||||
builder.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
builder.append("\"");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public final class JSONArray implements JSONValue, Iterable<JSONValue> {
|
||||
private final List<JSONValue> values;
|
||||
|
||||
public JSONArray(List<JSONValue> array) {
|
||||
this.values = array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONArray asArray() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONValue get(int i) {
|
||||
return values.get(i);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return values.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.append("[");
|
||||
for (var i = 0; i < size(); i++) {
|
||||
builder.append(get(i).toString());
|
||||
if (i != (size() - 1)) {
|
||||
builder.append(",");
|
||||
}
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<JSONValue> iterator() {
|
||||
return values.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
class JSONParser {
|
||||
private int pos = 0;
|
||||
private String input;
|
||||
|
||||
JSONParser() {
|
||||
}
|
||||
|
||||
private IllegalStateException failure(String message) {
|
||||
return new IllegalStateException(String.format("[%d]: %s : %s", pos, message, input));
|
||||
}
|
||||
|
||||
private char current() {
|
||||
return input.charAt(pos);
|
||||
}
|
||||
|
||||
private void advance() {
|
||||
pos++;
|
||||
}
|
||||
|
||||
private boolean hasInput() {
|
||||
return pos < input.length();
|
||||
}
|
||||
|
||||
private void expectMoreInput(String message) {
|
||||
if (!hasInput()) {
|
||||
throw failure(message);
|
||||
}
|
||||
}
|
||||
|
||||
private char next(String message) {
|
||||
advance();
|
||||
if (!hasInput()) {
|
||||
throw failure(message);
|
||||
}
|
||||
return current();
|
||||
}
|
||||
|
||||
private void expect(char c) {
|
||||
var msg = String.format("Expected character %c", c);
|
||||
|
||||
var n = next(msg);
|
||||
if (n != c) {
|
||||
throw failure(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONString parseBoolean() {
|
||||
if (current() == 't') {
|
||||
expect('r');
|
||||
expect('u');
|
||||
expect('e');
|
||||
advance();
|
||||
return new JSONString("true");
|
||||
}
|
||||
|
||||
if (current() == 'f') {
|
||||
expect('a');
|
||||
expect('l');
|
||||
expect('s');
|
||||
expect('e');
|
||||
advance();
|
||||
return new JSONString("false");
|
||||
}
|
||||
throw failure("a boolean can only be 'true' or 'false'");
|
||||
}
|
||||
|
||||
private JSONValue parseNumber() {
|
||||
var isInteger = true;
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (current() == '-') {
|
||||
builder.append(current());
|
||||
advance();
|
||||
expectMoreInput("a number cannot consist of only '-'");
|
||||
}
|
||||
|
||||
if (current() == '0') {
|
||||
builder.append(current());
|
||||
advance();
|
||||
|
||||
if (hasInput() && current() == '.') {
|
||||
isInteger = false;
|
||||
builder.append(current());
|
||||
advance();
|
||||
|
||||
expectMoreInput("a number cannot end with '.'");
|
||||
|
||||
if (!isDigit(current())) {
|
||||
throw failure("must be at least one digit after '.'");
|
||||
}
|
||||
|
||||
while (hasInput() && isDigit(current())) {
|
||||
builder.append(current());
|
||||
advance();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (hasInput() && isDigit(current())) {
|
||||
builder.append(current());
|
||||
advance();
|
||||
}
|
||||
|
||||
if (hasInput() && current() == '.') {
|
||||
isInteger = false;
|
||||
builder.append(current());
|
||||
advance();
|
||||
|
||||
expectMoreInput("a number cannot end with '.'");
|
||||
|
||||
if (!isDigit(current())) {
|
||||
throw failure("must be at least one digit after '.'");
|
||||
}
|
||||
|
||||
while (hasInput() && isDigit(current())) {
|
||||
builder.append(current());
|
||||
advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInput() && (current() == 'e' || current() == 'E')) {
|
||||
isInteger = false;
|
||||
|
||||
builder.append(current());
|
||||
advance();
|
||||
expectMoreInput("a number cannot end with 'e' or 'E'");
|
||||
|
||||
if (current() == '+' || current() == '-') {
|
||||
builder.append(current());
|
||||
advance();
|
||||
}
|
||||
|
||||
if (!isDigit(current())) {
|
||||
throw failure("a digit must follow {'e','E'}{'+','-'}");
|
||||
}
|
||||
|
||||
while (hasInput() && isDigit(current())) {
|
||||
builder.append(current());
|
||||
advance();
|
||||
}
|
||||
}
|
||||
|
||||
var value = builder.toString();
|
||||
if (isInteger) {
|
||||
Long.parseLong(value);
|
||||
return new JSONString(value);
|
||||
} else {
|
||||
Double.parseDouble(value);
|
||||
return new JSONString(value);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONString parseString() {
|
||||
var missingEndChar = "string is not terminated with '\"'";
|
||||
var builder = new StringBuilder();
|
||||
for (var c = next(missingEndChar); c != '"'; c = next(missingEndChar)) {
|
||||
if (c == '\\') {
|
||||
var n = next(missingEndChar);
|
||||
switch (n) {
|
||||
case '"':
|
||||
builder.append("\"");
|
||||
break;
|
||||
case '\\':
|
||||
builder.append("\\");
|
||||
break;
|
||||
case '/':
|
||||
builder.append("/");
|
||||
break;
|
||||
case 'b':
|
||||
builder.append("\b");
|
||||
break;
|
||||
case 'f':
|
||||
builder.append("\f");
|
||||
break;
|
||||
case 'n':
|
||||
builder.append("\n");
|
||||
break;
|
||||
case 'r':
|
||||
builder.append("\r");
|
||||
break;
|
||||
case 't':
|
||||
builder.append("\t");
|
||||
break;
|
||||
case 'u':
|
||||
var u1 = next(missingEndChar);
|
||||
var u2 = next(missingEndChar);
|
||||
var u3 = next(missingEndChar);
|
||||
var u4 = next(missingEndChar);
|
||||
var cp = Integer.parseInt(String.format("%c%c%c%c", u1, u2, u3, u4), 16);
|
||||
builder.append(new String(new int[]{cp}, 0, 1));
|
||||
break;
|
||||
default:
|
||||
throw failure(String.format("Unexpected escaped character '%c'", n));
|
||||
}
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
advance(); // step beyond closing "
|
||||
return new JSONString(builder.toString());
|
||||
}
|
||||
|
||||
private JSONArray parseArray() {
|
||||
var error = "array is not terminated with ']'";
|
||||
var list = new ArrayList<JSONValue>();
|
||||
|
||||
advance(); // step beyond opening '['
|
||||
consumeWhitespace();
|
||||
expectMoreInput(error);
|
||||
|
||||
while (current() != ']') {
|
||||
var val = parseValue();
|
||||
list.add(val);
|
||||
|
||||
expectMoreInput(error);
|
||||
if (current() == ',') {
|
||||
advance();
|
||||
}
|
||||
expectMoreInput(error);
|
||||
}
|
||||
|
||||
advance(); // step beyond closing ']'
|
||||
return new JSONArray(list);
|
||||
}
|
||||
|
||||
public JSONString parseNull() {
|
||||
expect('u');
|
||||
expect('l');
|
||||
expect('l');
|
||||
advance();
|
||||
return new JSONString(null);
|
||||
}
|
||||
|
||||
public JSONObject parseObject() {
|
||||
var error = "object is not terminated with '}'";
|
||||
var map = new HashMap<String, JSONValue>();
|
||||
|
||||
advance(); // step beyond opening '{'
|
||||
consumeWhitespace();
|
||||
expectMoreInput(error);
|
||||
|
||||
while (current() != '}') {
|
||||
var key = parseValue();
|
||||
if (!(key instanceof JSONString)) {
|
||||
throw failure("a field must of type string");
|
||||
}
|
||||
|
||||
if (!hasInput() || current() != ':') {
|
||||
throw failure("a field must be followed by ':'");
|
||||
}
|
||||
advance(); // skip ':'
|
||||
|
||||
var val = parseValue();
|
||||
map.put(key.asString(), val);
|
||||
|
||||
expectMoreInput(error);
|
||||
if (current() == ',') {
|
||||
advance();
|
||||
}
|
||||
expectMoreInput(error);
|
||||
}
|
||||
|
||||
advance(); // step beyond '}'
|
||||
return new JSONObject(map);
|
||||
}
|
||||
|
||||
private boolean isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
private boolean isStartOfNumber(char c) {
|
||||
return isDigit(c) || c == '-';
|
||||
}
|
||||
|
||||
private boolean isStartOfString(char c) {
|
||||
return c == '"';
|
||||
}
|
||||
|
||||
private boolean isStartOfBoolean(char c) {
|
||||
return c == 't' || c == 'f';
|
||||
}
|
||||
|
||||
private boolean isStartOfArray(char c) {
|
||||
return c == '[';
|
||||
}
|
||||
|
||||
private boolean isStartOfNull(char c) {
|
||||
return c == 'n';
|
||||
}
|
||||
|
||||
private boolean isWhitespace(char c) {
|
||||
return c == '\r' ||
|
||||
c == '\n' ||
|
||||
c == '\t' ||
|
||||
c == ' ';
|
||||
}
|
||||
|
||||
private boolean isStartOfObject(char c) {
|
||||
return c == '{';
|
||||
}
|
||||
|
||||
private void consumeWhitespace() {
|
||||
while (hasInput() && isWhitespace(current())) {
|
||||
advance();
|
||||
}
|
||||
}
|
||||
|
||||
public JSONValue parseValue() {
|
||||
JSONValue ret = null;
|
||||
|
||||
consumeWhitespace();
|
||||
if (hasInput()) {
|
||||
var c = current();
|
||||
|
||||
if (isStartOfNumber(c)) {
|
||||
ret = parseNumber();
|
||||
} else if (isStartOfString(c)) {
|
||||
ret = parseString();
|
||||
} else if (isStartOfBoolean(c)) {
|
||||
ret = parseBoolean();
|
||||
} else if (isStartOfArray(c)) {
|
||||
ret = parseArray();
|
||||
} else if (isStartOfNull(c)) {
|
||||
ret = parseNull();
|
||||
} else if (isStartOfObject(c)) {
|
||||
ret = parseObject();
|
||||
} else {
|
||||
throw failure("not a valid start of a JSON value");
|
||||
}
|
||||
}
|
||||
consumeWhitespace();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public JSONValue parse(String s) {
|
||||
if (s == null || s.equals("")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
input = s;
|
||||
|
||||
var result = parseValue();
|
||||
if (hasInput()) {
|
||||
throw failure("can only have one top-level JSON value");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONValue parse(String s) {
|
||||
return new JSONParser().parse(s);
|
||||
}
|
||||
|
||||
default int size() {
|
||||
throw new IllegalStateException("Size operation unsupported");
|
||||
}
|
||||
|
||||
default String asString() {
|
||||
throw new IllegalStateException("Unsupported conversion to String");
|
||||
}
|
||||
|
||||
default JSONArray asArray() {
|
||||
throw new IllegalStateException("Unsupported conversion to array");
|
||||
}
|
||||
|
||||
default JSONObject asObject() {
|
||||
throw new IllegalStateException("Unsupported conversion to object");
|
||||
}
|
||||
|
||||
default JSONValue get(String field) {
|
||||
return asObject().get(field);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,16 +31,13 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.Timestamp;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordedObject;
|
||||
import jdk.jfr.consumer.RecordingFile;
|
||||
import jdk.nashorn.api.scripting.JSObject;
|
||||
import jdk.jfr.tool.JSONValue.JSONArray;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
@ -51,8 +48,7 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
* @requires vm.hasJFR
|
||||
*
|
||||
* @library /test/lib /test/jdk
|
||||
* @modules jdk.scripting.nashorn
|
||||
* jdk.jfr
|
||||
* @modules jdk.jfr
|
||||
*
|
||||
* @run main/othervm jdk.jfr.tool.TestPrintJSON
|
||||
*/
|
||||
@ -65,42 +61,36 @@ public class TestPrintJSON {
|
||||
OutputAnalyzer output = ExecuteHelper.jfr("print", "--json", "--stack-depth", "999", recordingFile.toString());
|
||||
String json = output.getStdout();
|
||||
|
||||
// Parse JSON using Nashorn
|
||||
String statement = "var jsonObject = " + json;
|
||||
ScriptEngineManager factory = new ScriptEngineManager();
|
||||
ScriptEngine engine = factory.getEngineByName("nashorn");
|
||||
engine.eval(statement);
|
||||
JSObject o = (JSObject) engine.get("jsonObject");
|
||||
JSObject recording = (JSObject) o.getMember("recording");
|
||||
JSObject jsonEvents = (JSObject) recording.getMember("events");
|
||||
|
||||
JSONValue o = JSONValue.parse(json);
|
||||
JSONValue recording = o.get("recording");
|
||||
JSONArray jsonEvents = recording.get("events").asArray();
|
||||
List<RecordedEvent> events = RecordingFile.readAllEvents(recordingFile);
|
||||
Collections.sort(events, (e1, e2) -> e1.getEndTime().compareTo(e2.getEndTime()));
|
||||
// Verify events are equal
|
||||
Iterator<RecordedEvent> it = events.iterator();
|
||||
|
||||
for (Object jsonEvent : jsonEvents.values()) {
|
||||
for (JSONValue jsonEvent : jsonEvents) {
|
||||
RecordedEvent recordedEvent = it.next();
|
||||
String typeName = recordedEvent.getEventType().getName();
|
||||
Asserts.assertEquals(typeName, ((JSObject) jsonEvent).getMember("type").toString());
|
||||
Asserts.assertEquals(typeName, jsonEvent.get("type").asString());
|
||||
assertEquals(jsonEvent, recordedEvent);
|
||||
}
|
||||
Asserts.assertFalse(events.size() != jsonEvents.values().size(), "Incorrect number of events");
|
||||
Asserts.assertFalse(events.size() != jsonEvents.size(), "Incorrect number of events");
|
||||
}
|
||||
|
||||
private static void assertEquals(Object jsonObject, Object jfrObject) throws Exception {
|
||||
// Check object
|
||||
if (jfrObject instanceof RecordedObject) {
|
||||
JSObject values = (JSObject) ((JSObject) jsonObject).getMember("values");
|
||||
JSONValue values = ((JSONValue)jsonObject).get("values");
|
||||
RecordedObject recObject = (RecordedObject) jfrObject;
|
||||
Asserts.assertEquals(values.values().size(), recObject.getFields().size());
|
||||
Asserts.assertEquals(values.size(), recObject.getFields().size());
|
||||
for (ValueDescriptor v : recObject.getFields()) {
|
||||
String name = v.getName();
|
||||
Object jsonValue = values.getMember(name);
|
||||
Object jsonValue = values.get(name);
|
||||
Object expectedValue = recObject.getValue(name);
|
||||
if (v.getAnnotation(Timestamp.class) != null) {
|
||||
// Make instant of OffsetDateTime
|
||||
jsonValue = OffsetDateTime.parse("" + jsonValue).toInstant().toString();
|
||||
String text = ((JSONValue)jsonValue).asString();
|
||||
jsonValue = OffsetDateTime.parse(text).toInstant().toString();
|
||||
expectedValue = recObject.getInstant(name);
|
||||
}
|
||||
if (v.getAnnotation(Timespan.class) != null) {
|
||||
@ -113,9 +103,9 @@ public class TestPrintJSON {
|
||||
// Check array
|
||||
if (jfrObject != null && jfrObject.getClass().isArray()) {
|
||||
Object[] jfrArray = (Object[]) jfrObject;
|
||||
JSObject jsArray = (JSObject) jsonObject;
|
||||
JSONArray jsArray = ((JSONArray)jsonObject);
|
||||
for (int i = 0; i < jfrArray.length; i++) {
|
||||
assertEquals(jsArray.getSlot(i), jfrArray[i]);
|
||||
assertEquals(jsArray.get(i), jfrArray[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user