Skip to content

Commit a06bb9d

Browse files
committed
re-add Java subclassing
1 parent 64cdc03 commit a06bb9d

File tree

2 files changed

+56
-21
lines changed

2 files changed

+56
-21
lines changed

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

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
174174
import com.oracle.graal.python.nodes.object.GetClassNode;
175175
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
176+
import com.oracle.graal.python.nodes.statement.AbstractImportNode;
176177
import com.oracle.graal.python.nodes.subscript.SetItemNode;
177178
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
178179
import com.oracle.graal.python.nodes.util.CannotCastException;
@@ -197,6 +198,7 @@
197198
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
198199
import com.oracle.truffle.api.RootCallTarget;
199200
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
201+
import com.oracle.truffle.api.TruffleLanguage.Env;
200202
import com.oracle.truffle.api.TruffleLanguage.LanguageReference;
201203
import com.oracle.truffle.api.debug.Debugger;
202204
import com.oracle.truffle.api.dsl.Cached;
@@ -2051,6 +2053,12 @@ Object calculate(Object metatype, PTuple bases,
20512053
@Builtin(name = BuiltinNames.__BUILD_CLASS__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
20522054
@GenerateNodeFactory
20532055
public abstract static class BuildClassNode extends PythonVarargsBuiltinNode {
2056+
@TruffleBoundary
2057+
private static Object buildJavaClass(Object func, String name, Object base) {
2058+
Object module = AbstractImportNode.importModule(BuiltinNames.__GRAALPYTHON__);
2059+
Object buildFunction = PythonObjectLibrary.getUncached().lookupAttribute(module, null, "build_java_class");
2060+
return CallNode.getUncached().execute(buildFunction, func, name, base);
2061+
}
20542062

20552063
@Specialization
20562064
protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] arguments, PKeyword[] keywords,
@@ -2067,31 +2075,37 @@ protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] a
20672075
@Cached SetItemNode setOrigBases,
20682076
@Cached GetClassNode getClass) {
20692077

2078+
if (arguments.length < 1) {
2079+
throw raise(PythonErrorType.TypeError, "__build_class__: not enough arguments");
2080+
}
2081+
2082+
if (!PGuards.isFunction(function)) {
2083+
throw raise(PythonErrorType.TypeError, "__build_class__: func must be a function");
2084+
}
2085+
String name;
2086+
try {
2087+
name = CastToJavaStringNode.getUncached().execute(arguments[0]);
2088+
} catch (CannotCastException e) {
2089+
throw raise(PythonErrorType.TypeError, "__build_class__: name is not a string");
2090+
}
2091+
2092+
Object[] basesArray = Arrays.copyOfRange(arguments, 1, arguments.length);
2093+
PTuple origBases = factory.createTuple(basesArray);
2094+
2095+
Env env = PythonLanguage.getContext().getEnv();
2096+
if (arguments.length == 2 && env.isHostObject(arguments[1]) && env.asHostObject(arguments[1]) instanceof Class<?>) {
2097+
// we want to subclass a Java class
2098+
return buildJavaClass(function, name, arguments[1]);
2099+
}
2100+
20702101
class InitializeBuildClass {
2071-
String name;
20722102
boolean isClass;
20732103
Object meta;
20742104
PKeyword[] mkw;
20752105
PTuple bases;
2076-
PTuple origBases;
20772106

20782107
@TruffleBoundary
20792108
InitializeBuildClass() {
2080-
if (arguments.length < 1) {
2081-
throw raise(PythonErrorType.TypeError, "__build_class__: not enough arguments");
2082-
}
2083-
2084-
if (!PGuards.isFunction(function)) {
2085-
throw raise(PythonErrorType.TypeError, "__build_class__: func must be a function");
2086-
}
2087-
try {
2088-
name = CastToJavaStringNode.getUncached().execute(arguments[0]);
2089-
} catch (CannotCastException e) {
2090-
throw raise(PythonErrorType.TypeError, "__build_class__: name is not a string");
2091-
}
2092-
2093-
Object[] basesArray = Arrays.copyOfRange(arguments, 1, arguments.length);
2094-
origBases = factory.createTuple(basesArray);
20952109

20962110
bases = update.execute(origBases, basesArray, basesArray.length);
20972111

@@ -2137,7 +2151,7 @@ class InitializeBuildClass {
21372151
if (PGuards.isNoValue(prep)) {
21382152
ns = factory.createDict();
21392153
} else {
2140-
Object[] args = PGuards.isFunction(prep) ? new Object[]{init.name, init.bases} : new Object[]{init.meta, init.name, init.bases};
2154+
Object[] args = PGuards.isFunction(prep) ? new Object[]{name, init.bases} : new Object[]{init.meta, name, init.bases};
21412155
ns = callPrep.execute(frame, prep, args, init.mkw);
21422156
}
21432157
if (PGuards.isNoValue(getGetItem.execute(getGetItemClass.execute(ns)))) {
@@ -2148,10 +2162,10 @@ class InitializeBuildClass {
21482162
}
21492163
}
21502164
callBody.executeObject(frame, function, ns);
2151-
if (init.bases != init.origBases) {
2152-
setOrigBases.executeWith(frame, ns, SpecialAttributeNames.__ORIG_BASES__, init.origBases);
2165+
if (init.bases != origBases) {
2166+
setOrigBases.executeWith(frame, ns, SpecialAttributeNames.__ORIG_BASES__, origBases);
21532167
}
2154-
Object cls = callType.execute(frame, init.meta, new Object[]{init.name, init.bases, ns}, init.mkw);
2168+
Object cls = callType.execute(frame, init.meta, new Object[]{name, init.bases, ns}, init.mkw);
21552169

21562170
/*
21572171
* We could check here and throw "__class__ not set defining..." errors.

graalpython/lib-graalpython/__graalpython__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,24 @@ def import_current_as_named_module_with_delegate(module_name, delegate_name, del
148148
owner_module.__dict__.update(**wrapped_globals)
149149
if delegate_attributes:
150150
lazy_attributes_from_delegate(delegate_name, delegate_attributes, owner_module, on_import_error)
151+
152+
153+
@builtin
154+
def build_java_class(func, name, base):
155+
ns = {}
156+
func(ns) # fill up namespace with the methods and fields of the class
157+
ns['__super__'] = None # place where store the original java class when instance is created
158+
ExtenderClass = type("PythonJavaExtenderClass", (object, ), ns)
159+
HostAdapter = __graalpython__.extend(base)
160+
resultClass = type(name, (object, ), {})
161+
162+
def factory (cls, *args):
163+
# creates extender object and store the super java class
164+
extenderInstance = ExtenderClass()
165+
args = args[1:] + (extenderInstance, ) # remove the class and add the extender instance object
166+
hostObject = HostAdapter(*args) # create new adapter
167+
extenderInstance.__super__ = __graalpython__.super(hostObject) #set the super java object
168+
return hostObject
169+
170+
resultClass.__new__ = classmethod(factory)
171+
return resultClass

0 commit comments

Comments
 (0)