Skip to content

Commit b5fbaf6

Browse files
committed
[GR-31950] Improving Value->String conversion in Nashorn compatibility mode.
PullRequest: js/2032
2 parents a11f2e9 + 487f323 commit b5fbaf6

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

graal-js/src/com.oracle.truffle.js.scriptengine.test/src/com/oracle/truffle/js/scriptengine/test/TestNashornTypeConversion.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -84,6 +84,15 @@ private static void testToString(Object value, String expectedResult) throws Scr
8484
assertEquals(expectedResult, holder.getValue());
8585
}
8686

87+
private static void testToStringExpressionValue(String expression, String expectedResult) throws ScriptException {
88+
ScriptEngine engine = getEngineNashornCompat();
89+
ValueHolder holder = new ValueHolder();
90+
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
91+
bindings.put("holder", holder);
92+
engine.eval("holder.setValue(" + expression + ")");
93+
assertEquals(expectedResult, holder.getValue());
94+
}
95+
8796
@Test
8897
public void testNumberToString() throws ScriptException {
8998
testToString(42, "42");
@@ -100,6 +109,41 @@ public void testObjectToString() throws ScriptException {
100109
testToString(p, p.toString());
101110
}
102111

112+
@Test
113+
public void testRegExpToString() throws ScriptException {
114+
testToStringExpressionValue("/foo/i", "/foo/i");
115+
}
116+
117+
@Test
118+
public void testArrayToString() throws ScriptException {
119+
testToStringExpressionValue("[42,211]", "42,211");
120+
}
121+
122+
@Test
123+
public void testObjectToStringToString() throws ScriptException {
124+
testToStringExpressionValue("({ toString: function() { return 'foo'; } })", "foo");
125+
}
126+
127+
@Test
128+
public void testObjectValueOfToString() throws ScriptException {
129+
testToStringExpressionValue("Object.setPrototypeOf({ valueOf: function() { return 42; } }, null)", "42");
130+
}
131+
132+
@Test
133+
public void testBooleanObjectToString() throws ScriptException {
134+
testToStringExpressionValue("Object(true)", "true");
135+
}
136+
137+
@Test
138+
public void testNumberObjectToString() throws ScriptException {
139+
testToStringExpressionValue("Object(3.14)", "3.14");
140+
}
141+
142+
@Test
143+
public void testStringObjectToString() throws ScriptException {
144+
testToStringExpressionValue("Object('xyz')", "xyz");
145+
}
146+
103147
@Test
104148
public void testNumberToDouble() throws ScriptException {
105149
ScriptEngine engine = getEngineNashornCompat();

graal-js/src/com.oracle.truffle.js.scriptengine/src/com/oracle/truffle/js/scriptengine/GraalJSScriptEngine.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -101,7 +101,7 @@ public final class GraalJSScriptEngine extends AbstractScriptEngine implements C
101101
private static HostAccess createNashornHostAccess() {
102102
HostAccess.Builder b = HostAccess.newBuilder(HostAccess.ALL);
103103
// Last resort conversions similar to those in NashornBottomLinker.
104-
b.targetTypeMapping(Value.class, String.class, v -> !v.isNull(), Value::toString, TargetMappingPrecedence.LOWEST);
104+
b.targetTypeMapping(Value.class, String.class, v -> !v.isNull(), v -> toString(v), TargetMappingPrecedence.LOWEST);
105105
b.targetTypeMapping(Number.class, Integer.class, n -> true, n -> n.intValue(), TargetMappingPrecedence.LOWEST);
106106
b.targetTypeMapping(Number.class, Double.class, n -> true, n -> n.doubleValue(), TargetMappingPrecedence.LOWEST);
107107
b.targetTypeMapping(Number.class, Long.class, n -> true, n -> n.longValue(), TargetMappingPrecedence.LOWEST);
@@ -110,6 +110,35 @@ private static HostAccess createNashornHostAccess() {
110110
return b.build();
111111
}
112112

113+
// ToString() operation
114+
private static String toString(Value value) {
115+
return toPrimitive(value).toString();
116+
}
117+
118+
// "Type(result) is not Object" heuristic for the purpose of ToPrimitive() conversion
119+
private static boolean isPrimitive(Value value) {
120+
return value.isString() || value.isNumber() || value.isBoolean() || value.isNull();
121+
}
122+
123+
// ToPrimitive()/OrdinaryToPrimitive() operation
124+
private static Value toPrimitive(Value value) {
125+
if (value.hasMembers()) {
126+
for (String methodName : new String[]{"toString", "valueOf"}) {
127+
if (value.canInvokeMember(methodName)) {
128+
Value maybePrimitive = value.invokeMember(methodName);
129+
if (isPrimitive(maybePrimitive)) {
130+
return maybePrimitive;
131+
}
132+
}
133+
}
134+
}
135+
if (isPrimitive(value)) {
136+
return value;
137+
} else {
138+
throw new ClassCastException();
139+
}
140+
}
141+
113142
private static boolean toBoolean(double d) {
114143
return d != 0.0 && !Double.isNaN(d);
115144
}

0 commit comments

Comments
 (0)