diff --git a/javatools/src/main/java/org/xvm/runtime/ServiceContext.java b/javatools/src/main/java/org/xvm/runtime/ServiceContext.java index 7c8fb977b6..65111365cd 100644 --- a/javatools/src/main/java/org/xvm/runtime/ServiceContext.java +++ b/javatools/src/main/java/org/xvm/runtime/ServiceContext.java @@ -22,14 +22,20 @@ import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + import org.xvm.asm.ConstantPool; +import org.xvm.asm.GenericTypeResolver; import org.xvm.asm.LinkerContext; import org.xvm.asm.MethodStructure; import org.xvm.asm.Op; +import org.xvm.asm.constants.FormalConstant; +import org.xvm.asm.constants.MethodConstant; import org.xvm.asm.constants.PropertyConstant; import org.xvm.asm.constants.SingletonConstant; import org.xvm.asm.constants.TypeConstant; +import org.xvm.asm.constants.TypeParameterConstant; import org.xvm.asm.op.Return_0; @@ -1196,7 +1202,7 @@ public int sendOp1Request(Frame frame, Op op, int iReturn, TypeConstant... typeR { assert iReturn != Op.A_IGNORE_ASYNC; - OpRequest request = new OpRequest(frame, op, iReturn == Op.A_IGNORE ? 0 : 1, typeRet); + OpRequest request = new OpRequest(frame, op, iReturn == Op.A_IGNORE ? 0 : 1, () -> typeRet); addRequest(request, frame.isDynamicVar(iReturn)); @@ -1329,8 +1335,11 @@ public String toString() } }; - TypeConstant[] atypeRet = cReturns == 0 ? TypeConstant.NO_TYPES : hFunction.getReturnTypes(); - OpRequest request = new OpRequest(frame, opCall, cReturns, atypeRet); + + Supplier supplier = cReturns == 0 + ? null + : () -> resolveFormalReturnTypes(hFunction, ahArg); + OpRequest request = new OpRequest(frame, opCall, cReturns, supplier); boolean fOverwhelmed = addRequest(request, fAsync); @@ -1395,8 +1404,8 @@ public String toString() } }; - TypeConstant[] atypeRet = hFunction.getReturnTypes(); - OpRequest request = new OpRequest(frame, opCall, cReturns, atypeRet); + Supplier supplier = () -> resolveFormalReturnTypes(hFunction, ahArg); + OpRequest request = new OpRequest(frame, opCall, cReturns, supplier); CompletableFuture future = request.f_future; boolean fOverwhelmed = addRequest(request, false); @@ -1422,6 +1431,71 @@ public String toString() return frame.call(frame.createWaitFrame(future, aiReturn)); } + /** + * Helper method to resolve the formal type parameters in the function's return type. + * + * This method is only called if the value returned by the service is not immutable and needs + * to be proxied. In that case the actual return value type could be used to resolve generic + * return types (see ClassTemplate.createProxyHandle), but formal type parameters can only be + * resolved using the type parameters types that are passed in by the caller. + */ + private TypeConstant[] resolveFormalReturnTypes(FunctionHandle hFunction, ObjectHandle[] ahArg) + { + TypeConstant[] atype = hFunction.getReturnTypes(); + int cTypes = atype.length; + + if (cTypes > 0) + { + GenericTypeResolver resolver = new GenericTypeResolver() + { + @Override + public TypeConstant resolveGenericType(String sFormalName) + { + return null; + } + + @Override + public TypeConstant resolveFormalType(FormalConstant constFormal) + { + if (constFormal instanceof TypeParameterConstant constTypeParam) + { + int nRegister = constTypeParam.getRegister(); + MethodConstant idMethod = hFunction.getMethodId(); + MethodStructure method = idMethod == null + ? null + : (MethodStructure) idMethod.getComponent(); + if (method != null && nRegister < method.getTypeParamCount() && + method.getParam(nRegister).getName().equals(constTypeParam.getName())) + { + return ahArg[nRegister].getType().getParamType(0); + } + } + + return null; + } + }; + + boolean fClone = true; + for (int i = 0; i < cTypes; i++) + { + TypeConstant type = atype[i]; + if (type.containsTypeParameter(true)) + { + TypeConstant typeResolved = type.resolveGenerics(f_pool, resolver); + if (typeResolved != type) + { + if (fClone) + { + atype = atype.clone(); + } + atype[i] = typeResolved; + } + } + } + }; + return atype; + } + /** * Send an asynchronous property "read" operation request. * @@ -1547,7 +1621,7 @@ public String toString() } }; - OpRequest request = new OpRequest(frame, opInit, 1); + OpRequest request = new OpRequest(frame, opInit, 1, null); addRequest(request, false); @@ -1740,13 +1814,17 @@ protected void sendResponse(Fiber fiberCaller, Frame frame, CompletableFuture fu public static class OpRequest extends Request { - protected OpRequest(Frame frameCaller, Op op, int cReturns, TypeConstant... typeRet) + /** + * @param supplierRet (optional) the supplier of return types to be used *only* if the + * request's return values need to be proxied + */ + protected OpRequest(Frame frameCaller, Op op, int cReturns, Supplier supplierRet) { super(frameCaller); f_op = op; - f_atypeReturn = typeRet; f_cReturns = cReturns; + f_supplierRet = supplierRet; } @Override @@ -1803,7 +1881,8 @@ protected int checkResponse(Fiber fiberCaller, Frame frame, int cReturns, int in if (frame.m_hException == null) { int iResult = ctxSrc.validatePassThrough(frame, ctxDst, - f_atypeReturn, frame.f_ahVar, 1); + f_supplierRet == null ? null : f_supplierRet.get(), + frame.f_ahVar, 1); if (iResult == Op.R_EXCEPTION) { Arrays.fill(frame.f_ahVar, null); @@ -1873,7 +1952,9 @@ protected int checkResponse(Fiber fiberCaller, Frame frame, int cReturns, int in } } - int iResult = ctxSrc.validatePassThrough(frame, ctxDst, null, ahReturn, cReturns); + int iResult = ctxSrc.validatePassThrough(frame, ctxDst, + f_supplierRet == null ? null : f_supplierRet.get(), + ahReturn, cReturns); if (iResult == Op.R_EXCEPTION) { Arrays.fill(ahReturn, null); @@ -1892,9 +1973,9 @@ public String toString() return f_op.toString(); } - private final Op f_op; - private final int f_cReturns; - private final TypeConstant[] f_atypeReturn; + private final Op f_op; + private final int f_cReturns; + private final Supplier f_supplierRet; } /** diff --git a/manualTests/src/main/x/TestSimple.x b/manualTests/src/main/x/TestSimple.x index 8a3cadc85e..c9051629c4 100644 --- a/manualTests/src/main/x/TestSimple.x +++ b/manualTests/src/main/x/TestSimple.x @@ -1,19 +1,16 @@ -module TestSimple { +module TestSimple.examples.org { @Inject Console console; - package json import json.xtclang.org; + void run() { + String[] strings = ["abc", "def"]; - import json.*; + Test t = new Test(strings); + String[] filtered = t.filter(s -> s.indexOf("e")).toArray(Constant); // that used to fail at RT + console.print(filtered); + } - void run() { - String str = \|{ - |"host":"admin.xqiz.it", - |"http":8080, - |"https":8090 - |} - ; - Doc doc = new Parser(str.toReader()).parseDoc(); // used to fail to compile (unknown type "Doc") - assert doc.is(Map); - console.print(doc); + service Test(Collection underlying) + implements Collection + delegates Collection(underlying) { } } \ No newline at end of file