Skip to content

Commit bebf304

Browse files
committed
Make foreign string inherit from str
1 parent 7f1995a commit bebf304

File tree

18 files changed

+352
-578
lines changed

18 files changed

+352
-578
lines changed

graalpython/com.oracle.graal.python.tck/src/com/oracle/graal/python/tck/PythonProvider.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -500,34 +500,34 @@ public void accept(SnippetRun snippetRun) throws PolyglotException {
500500
List<? extends Value> parameters = snippetRun.getParameters();
501501
assert parameters.size() == 2;
502502

503-
Value par0 = parameters.get(0);
504-
Value par1 = parameters.get(1);
503+
Value self = parameters.get(0);
504+
Value index = parameters.get(1);
505505

506506
long len = -1;
507507

508-
if (par0.hasArrayElements()) {
509-
len = par0.getArraySize();
510-
} else if (par0.isString()) {
511-
len = par0.asString().length();
508+
if (self.isString()) {
509+
len = self.asString().length();
510+
} else if (self.hasArrayElements()) {
511+
len = self.getArraySize();
512512
}
513513
if (len >= 0) {
514514
int idx;
515-
if (par1.isBoolean()) {
516-
idx = par1.asBoolean() ? 1 : 0;
517-
} else if (par1.isNumber() && par1.fitsInInt()) {
518-
idx = par1.asInt();
515+
if (index.isBoolean()) {
516+
idx = index.asBoolean() ? 1 : 0;
517+
} else if (index.isNumber() && index.fitsInInt()) {
518+
idx = index.asInt();
519519
} else {
520-
assert snippetRun.getException() != null;
520+
assert snippetRun.getException() != null : snippetRun.getResult();
521521
return;
522522
}
523-
if ((idx >= 0 && len > idx) || (idx < 0 && idx + len >= 0 && len > idx + len)) {
524-
assert snippetRun.getException() == null : snippetRun.getException().toString();
523+
if ((idx >= 0 && idx < len) || (idx < 0 && idx + len >= 0 && idx + len < len)) {
524+
assert snippetRun.getException() == null : snippetRun.getException();
525525
} else {
526-
assert snippetRun.getException() != null;
526+
assert snippetRun.getException() != null : snippetRun.getResult();
527527
}
528-
} else if (par0.hasHashEntries()) {
529-
if (par1.getMetaObject() != null) {
530-
String metaName = par1.getMetaObject().getMetaQualifiedName();
528+
} else if (self.hasHashEntries()) {
529+
if (index.getMetaObject() != null) {
530+
String metaName = index.getMetaObject().getMetaQualifiedName();
531531
for (String s : UNHASHABLE_TYPES) {
532532
if (metaName.equals(s)) {
533533
// those don't work, but that's expected
@@ -536,11 +536,11 @@ public void accept(SnippetRun snippetRun) throws PolyglotException {
536536
}
537537
}
538538
}
539-
Value v = par0.getHashValueOrDefault(par1, PythonProvider.class.getName());
539+
Value v = self.getHashValueOrDefault(index, PythonProvider.class.getName());
540540
if (v.isString() && v.asString().equals(PythonProvider.class.getName())) {
541-
assert snippetRun.getException() != null;
541+
assert snippetRun.getException() != null : snippetRun.getResult();
542542
} else {
543-
assert snippetRun.getException() == null : snippetRun.getException().toString();
543+
assert snippetRun.getException() == null : snippetRun.getException();
544544
}
545545
} else {
546546
// argument type error, rethrow

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ def foo(obj):
496496
foo.execute(ProxyObject.fromMap(m));
497497
}
498498

499-
public class JavaObject {
499+
public static class JavaObject {
500500
public byte byteValue = 1;
501501
public short shortValue = 2;
502502
public int intValue = 3;
@@ -563,7 +563,7 @@ public void accessJavaObjectFields() throws IOException {
563563
"5.0 <class 'float'>\n" +
564564
"6.0 <class 'float'>\n" +
565565
"True <class 'bool'>\n" +
566-
"c <class 'str'>\n", out.toString("UTF-8"));
566+
"c <class 'polyglot.ForeignString'>\n", out.toString("UTF-8"));
567567
}
568568

569569
@Test
@@ -590,7 +590,7 @@ public void accessJavaObjectGetters() throws IOException {
590590
"5.0 <class 'float'>\n" +
591591
"6.0 <class 'float'>\n" +
592592
"True <class 'bool'>\n" +
593-
"c <class 'str'>\n", out.toString("UTF-8"));
593+
"c <class 'polyglot.ForeignString'>\n", out.toString("UTF-8"));
594594
}
595595

596596
@Test

graalpython/com.oracle.graal.python.test/src/tests/test_interop.py

Lines changed: 113 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,119 @@ def foo(a, b):
10321032
assert list(h.keys()) == ['b', 'a']
10331033
assert foo(**h) == [1, 2]
10341034

1035+
def test_java_string(self):
1036+
from java.lang import String, Character
1037+
1038+
def wrap(string):
1039+
return __graalpython__.foreign_wrapper(String(string))
1040+
# we have to wrap otherwise the string is passed as a j.l.String to Python
1041+
s = wrap("ab")
1042+
empty = wrap("")
1043+
1044+
assert isinstance(s, str)
1045+
assert str in type(s).mro()
1046+
self.assertEqual(['ForeignString', 'str', 'foreign', 'object'], [t.__name__ for t in type(s).mro()])
1047+
assert repr(s) == repr("ab")
1048+
assert str(s) == str("ab"), str(s)
1049+
assert bool(s) == True
1050+
assert s
1051+
1052+
assert bool(empty) == False
1053+
assert not empty
1054+
1055+
c = Character.valueOf(ord("A"))
1056+
self.assertEqual(['ForeignString', 'str', 'foreign', 'object'], [t.__name__ for t in type(c).mro()])
1057+
assert repr(c) == repr("A")
1058+
assert str(c) == str("A"), str(s)
1059+
assert c
1060+
1061+
assert type(str(s)) is str
1062+
1063+
# Ensure java.lang.String members are not visible through foreign_wrapper()
1064+
with self.assertRaisesRegex(AttributeError, "foreign object has no attribute 'toLowerCase'"):
1065+
s.toLowerCase()
1066+
1067+
assert s + "cd" == "abcd"
1068+
assert "cd" + s == "cdab"
1069+
assert s + s == "abab"
1070+
1071+
assert "a" in s
1072+
assert "z" not in s
1073+
1074+
assert s == "ab"
1075+
assert s != "cd"
1076+
1077+
assert wrap("B") > wrap("A")
1078+
assert wrap("A") < "B"
1079+
assert "B" > wrap("A")
1080+
1081+
assert s[0] == "a"
1082+
assert s[1] == "b"
1083+
assert s[-2] == "a"
1084+
assert s[-1] == "b"
1085+
1086+
assert s[0:2] == "ab"
1087+
assert wrap("abcd")[1:3] == "bc"
1088+
1089+
assert type(hash(s)) == int
1090+
1091+
assert s * 3 == "ababab"
1092+
assert 3 * s == "ababab"
1093+
1094+
assert wrap("%03d") % 42 == "042"
1095+
1096+
assert [e for e in s] == ['a', 'b']
1097+
1098+
assert len(s) == 2
1099+
1100+
assert s.capitalize() == "Ab"
1101+
assert wrap("AbC").casefold() == "abc"
1102+
assert s.center(4) == " ab "
1103+
assert s.count(wrap("a")) == 1
1104+
assert s.encode() == b'ab'
1105+
assert wrap("é").encode(wrap('ISO-8859-1')) == b'\xe9'
1106+
assert s.endswith(wrap("b"))
1107+
assert wrap("\t").expandtabs(tabsize=2) == " "
1108+
assert s.find(wrap("b")) == 1
1109+
assert wrap(">{}<").format(s) == ">ab<"
1110+
assert wrap("{x}").format_map({wrap("x"): wrap("42")}) == "42"
1111+
assert s.index(wrap("b")) == 1
1112+
assert s.isalnum()
1113+
assert s.isalpha()
1114+
assert s.isascii()
1115+
assert not s.isdecimal()
1116+
assert not s.isdigit()
1117+
assert s.isidentifier()
1118+
assert s.islower()
1119+
assert not s.isnumeric()
1120+
assert s.isprintable()
1121+
assert not s.isspace()
1122+
assert not s.istitle()
1123+
assert not s.isupper()
1124+
assert wrap(",").join([s, "cd"]) == "ab,cd"
1125+
assert s.ljust(3) == "ab "
1126+
assert wrap("AB").lower() == "ab"
1127+
assert wrap(" a ").lstrip() == "a "
1128+
assert wrap("ab,cd,ef").partition(wrap(",")) == ("ab", ",", "cd,ef")
1129+
assert s.removeprefix(wrap("a")) == "b"
1130+
assert s.removesuffix(wrap("b")) == "a"
1131+
assert wrap("aba").replace(wrap("a"), wrap("z")) == "zbz"
1132+
assert s.rfind(wrap("a")) == 0
1133+
assert s.rindex(wrap("a")) == 0
1134+
assert s.rjust(3) == " ab"
1135+
assert wrap("ab,cd,ef").rpartition(wrap(",")) == ("ab,cd", ",", "ef")
1136+
assert wrap("ab,cd,ef").rsplit(wrap(","), 1) == ["ab,cd", "ef"]
1137+
assert wrap(" a ").rstrip() == " a"
1138+
assert wrap("ab,cd,ef").split(wrap(","), 1) == ["ab", "cd,ef"]
1139+
assert wrap("ab\ncd").splitlines() == ["ab", "cd"]
1140+
assert s.startswith("a")
1141+
assert wrap(" a ").strip() == "a"
1142+
assert wrap("Ab").swapcase() == "aB"
1143+
assert wrap("a title").title() == "A Title"
1144+
assert s.translate({ ord("a"): wrap("ZZ") }) == "ZZb"
1145+
assert s.upper() == "AB"
1146+
assert s.zfill(5) == "000ab"
1147+
10351148
def test_foreign_number_list(self):
10361149
from java.util import ArrayList
10371150
# Like c(42) in R
@@ -1069,14 +1182,6 @@ def test_foreign_number_list(self):
10691182
assert l > n
10701183
assert n < l
10711184

1072-
def test_foreign_string(self):
1073-
s = __graalpython__.foreign_wrapper("ab")
1074-
1075-
self.assertEqual(['ForeignString', 'foreign', 'object'], [t.__name__ for t in type(s).mro()])
1076-
1077-
assert s * 3 == "ababab"
1078-
assert 3 * s == "ababab"
1079-
10801185
def test_foreign_repl(self):
10811186
from java.util.logging import LogRecord
10821187
from java.util.logging import Level

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.DICT_M_FLAGS;
4545
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.FLOAT_M_FLAGS;
4646
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.FOREIGNNUMBER_M_FLAGS;
47-
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.FOREIGNOBJECT_M_FLAGS;
4847
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.FROZENSET_M_FLAGS;
4948
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.GENERATOR_M_FLAGS;
5049
import static com.oracle.graal.python.builtins.objects.type.MethodsFlags.GENERIC_ALIAS_M_FLAGS;
@@ -299,7 +298,7 @@ public enum PythonBuiltinClassType implements TruffleObject {
299298
PickleBuffer("PickleBuffer", "_pickle"),
300299

301300
// Foreign
302-
ForeignObject(J_FOREIGN, Flags.PRIVATE_BASE_WDICT, FOREIGNOBJECT_M_FLAGS, ForeignObjectBuiltins.SLOTS),
301+
ForeignObject(J_FOREIGN, Flags.PRIVATE_BASE_WDICT, ForeignObjectBuiltins.SLOTS),
303302
ForeignNumber("ForeignNumberType", Flags.PRIVATE_BASE_WDICT, FOREIGNNUMBER_M_FLAGS, ForeignNumberBuiltins.SLOTS),
304303

305304
// bz2

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,13 +1203,75 @@ Object foreignWrapper(Object object) {
12031203
return new ForeignWrapper(object);
12041204
}
12051205

1206+
@SuppressWarnings("unused")
12061207
@ExportLibrary(value = InteropLibrary.class, delegateTo = "object")
12071208
static final class ForeignWrapper implements TruffleObject {
12081209
final Object object;
12091210

12101211
ForeignWrapper(Object object) {
12111212
this.object = object;
12121213
}
1214+
1215+
/*
1216+
* Hide members as we want to treat the object solely by using its interop traits &
1217+
* trait-specific messages and not unintentionally read or invoke one of its members
1218+
* (methods or fields).
1219+
*/
1220+
1221+
@ExportMessage
1222+
boolean hasMembers() {
1223+
return false;
1224+
}
1225+
1226+
@ExportMessage
1227+
boolean isMemberReadable(String member) {
1228+
return false;
1229+
}
1230+
1231+
@ExportMessage
1232+
boolean isMemberModifiable(String member) {
1233+
return false;
1234+
}
1235+
1236+
@ExportMessage
1237+
boolean isMemberInsertable(String member) {
1238+
return false;
1239+
}
1240+
1241+
@ExportMessage
1242+
boolean isMemberRemovable(String member) {
1243+
return false;
1244+
}
1245+
1246+
@ExportMessage
1247+
boolean isMemberInvocable(String member) {
1248+
return false;
1249+
}
1250+
1251+
@ExportMessage
1252+
Object getMembers(boolean includeInternal) throws UnsupportedMessageException {
1253+
throw UnsupportedMessageException.create();
1254+
}
1255+
1256+
@ExportMessage
1257+
Object readMember(String member) throws UnsupportedMessageException {
1258+
throw UnsupportedMessageException.create();
1259+
}
1260+
1261+
@ExportMessage
1262+
void writeMember(String member, Object value) throws UnsupportedMessageException {
1263+
throw UnsupportedMessageException.create();
1264+
}
1265+
1266+
@ExportMessage
1267+
void removeMember(String member) throws UnsupportedMessageException {
1268+
throw UnsupportedMessageException.create();
1269+
}
1270+
1271+
@ExportMessage
1272+
Object invokeMember(String member, Object[] arguments) throws UnsupportedMessageException {
1273+
throw UnsupportedMessageException.create();
1274+
}
12131275
}
12141276
}
12151277
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/UnionTypeBuiltins.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
6666
import com.oracle.truffle.api.dsl.Bind;
6767
import com.oracle.truffle.api.dsl.Cached;
68+
import com.oracle.truffle.api.dsl.Cached.Exclusive;
6869
import com.oracle.truffle.api.dsl.Cached.Shared;
6970
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
7071
import com.oracle.truffle.api.dsl.NodeFactory;
@@ -98,7 +99,7 @@ protected abstract static class SetattrNode extends SetAttrBuiltinNode {
9899
@Specialization
99100
static void doStringKey(VirtualFrame frame, Object object, TruffleString key, Object value,
100101
@Bind("this") Node inliningTarget,
101-
@Shared @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode,
102+
@Exclusive @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode,
102103
@Shared @Cached WriteAttributeToObjectNode write,
103104
@Shared @Cached TruffleString.EqualNode equalNode,
104105
@Shared @Cached PyCStructUnionTypeUpdateStgDict updateStgDict,
@@ -107,13 +108,14 @@ static void doStringKey(VirtualFrame frame, Object object, TruffleString key, Ob
107108
updateStgDictIfNecessary(frame, object, key, value, equalNode, updateStgDict, factory);
108109
}
109110

111+
// @Exclusive to address warning
110112
@Specialization
111113
@InliningCutoff
112114
static void doGenericKey(VirtualFrame frame, Object object, Object keyObject, Object value,
113115
@Bind("this") Node inliningTarget,
114116
@Cached CastToTruffleStringNode castKeyNode,
115117
@Cached PRaiseNode.Lazy raiseNode,
116-
@Shared @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode,
118+
@Exclusive @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode,
117119
@Shared @Cached WriteAttributeToObjectNode write,
118120
@Shared @Cached TruffleString.EqualNode equalNode,
119121
@Shared @Cached PyCStructUnionTypeUpdateStgDict updateStgDict,

0 commit comments

Comments
 (0)