diff --git a/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java b/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java index 0451f9eaba..089aea6a89 100644 --- a/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java +++ b/java/src/com/google/template/soy/jbcsrc/ExpressionCompiler.java @@ -1615,7 +1615,7 @@ private SoyExpression visitFieldAccess(SoyExpression baseExpr, FieldAccessNode n if (sourceMethod != null) { Optional externApi = ExternAdaptors.asExtern(sourceMethod, args); if (externApi.isPresent()) { - return callExtern(externApi.get(), args); + return callExtern(externApi.get(), args, node.getType()); } return sourceFunctionCompiler.compile(node, sourceMethod, args, parameters, detacher); } @@ -1771,7 +1771,7 @@ var record = (RecordLiteralNode) node.getChild(1); visitAllParams(node, ImmutableList.builder().add(baseExpr)); Optional externApi = ExternAdaptors.asExtern(sourceMethod, args); if (externApi.isPresent()) { - return callExtern(externApi.get(), args); + return callExtern(externApi.get(), args, node.getType()); } return sourceFunctionCompiler.compile(node, sourceMethod, args, parameters, detacher); } @@ -2023,13 +2023,14 @@ SoyExpression visitPluginFunction(FunctionNode node) { return callExtern( ExternAdaptors.asExtern( (SoyJavaExternFunction) fn, args, node.getType(), node.getAllowedParamTypes()), - args); + args, + node.getType()); } else if (fn instanceof SoyJavaSourceFunction) { ImmutableList args = visitAllParams(node); return sourceFunctionCompiler.compile( node, (SoyJavaSourceFunction) fn, args, parameters, detacher); } else if (fn instanceof Extern) { - return callExtern((Extern) fn, visitAllParams(node)); + return callExtern((Extern) fn, visitAllParams(node), node.getType()); } else if (fn == FunctionNode.FUNCTION_POINTER) { FunctionType functionType = node.getNameExpr().getType().asType(FunctionType.class); SoyRuntimeType soyReturnType = ExternCompiler.getRuntimeType(functionType.getReturnType()); @@ -2107,7 +2108,8 @@ private ImmutableList visitAllParams( return builder.build(); } - private SoyExpression callExtern(Extern extern, List params) { + private SoyExpression callExtern( + Extern extern, List params, SoyType resolvedReturnType) { SourceLogicalPath path = extern.getPath(); JavaImpl javaImpl = extern.getJavaImpl(); boolean hasJavaImpl = javaImpl != null || extern.hasAutoImpl(); @@ -2125,7 +2127,7 @@ private SoyExpression callExtern(Extern extern, List params) { linkStatically = hasJavaImpl && owningFile.getSoyFileKind() == SoyFileKind.SRC; } FunctionType functionType = extern.getSignature(); - SoyRuntimeType soyReturnType = ExternCompiler.getRuntimeType(functionType.getReturnType()); + SoyRuntimeType soyReturnType = ExternCompiler.getRuntimeType(resolvedReturnType); boolean requiresRenderContext = !linkStatically || requiresRenderContext(extern); if (extern.isJavaAsync()) { @@ -2199,7 +2201,7 @@ private SoyExpression callExtern(Extern extern, List params) { externCall = ExternCompiler.adaptReturnType( getTypeInfoForJavaImpl(javaImpl.returnType().className()).type(), - functionType.getReturnType(), + resolvedReturnType, externCall); // Allow ExternSourceFunction to return a boxed value. if (isDefinitelyAssignableFrom(SOY_VALUE_TYPE, externCall.resultType())) { @@ -2214,8 +2216,12 @@ private SoyExpression callExtern(Extern extern, List params) { Method asmMethod = ExternCompiler.buildMemberMethod( extern.getName(), functionType, requiresRenderContext, extern.isJavaAsync()); + + Type descriptorReturnType = Type.getReturnType(asmMethod.getDescriptor()); + Type expressionReturnType = soyReturnType.runtimeType(); + externCall = - new Expression(soyReturnType.runtimeType()) { + new Expression(expressionReturnType) { @Override protected void doGen(CodeBuilder adapter) { for (var arg : args) { @@ -2227,6 +2233,39 @@ protected void doGen(CodeBuilder adapter) { CALL_EXTERN_HANDLE, externOwner.className(), asmMethod.getName()); + + // Unbox if needed + if (!descriptorReturnType.equals(expressionReturnType)) { + if (expressionReturnType.equals(Type.LONG_TYPE)) { + adapter.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "com/google/template/soy/data/SoyValue", + "longValue", + "()J", + false); + } else if (expressionReturnType.equals(Type.INT_TYPE)) { + adapter.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "com/google/template/soy/data/SoyValue", + "integerValue", + "()I", + false); + } else if (expressionReturnType.equals(Type.DOUBLE_TYPE)) { + adapter.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "com/google/template/soy/data/SoyValue", + "floatValue", + "()D", + false); + } else if (expressionReturnType.equals(Type.BOOLEAN_TYPE)) { + adapter.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + "com/google/template/soy/data/SoyValue", + "booleanValue", + "()Z", + false); + } + } } }; } diff --git a/java/src/com/google/template/soy/jbcsrc/ExpressionDetacher.java b/java/src/com/google/template/soy/jbcsrc/ExpressionDetacher.java index c01795f1bc..f802b44505 100644 --- a/java/src/com/google/template/soy/jbcsrc/ExpressionDetacher.java +++ b/java/src/com/google/template/soy/jbcsrc/ExpressionDetacher.java @@ -182,6 +182,11 @@ protected void doGen(CodeBuilder cb) { cb.ifZCmp(Opcodes.IFNE, end); // Stack: SVP, RR saveOperation.gen(cb); + System.out.println("Jetski: BasicDetacher in method: " + cb.getThisMethodName()); + if (cb.getReturnType().getSort() == org.objectweb.asm.Type.VOID) { + cb.pop(); // Pop RR + cb.pop(); // Pop SVP + } cb.returnValue(); cb.mark(end); cb.pop(); // Stack: SVP diff --git a/java/src/com/google/template/soy/jbcsrc/LazyClosureCompiler.java b/java/src/com/google/template/soy/jbcsrc/LazyClosureCompiler.java index 62e5a2c938..926785a6ac 100644 --- a/java/src/com/google/template/soy/jbcsrc/LazyClosureCompiler.java +++ b/java/src/com/google/template/soy/jbcsrc/LazyClosureCompiler.java @@ -497,24 +497,24 @@ private static ExpressionDetacher.BasicDetacher createOptimisticDetacher( new Statement(Statement.Kind.TERMINAL) { @Override protected void doGen(CodeBuilder cb) { - cb.visitVarInsn(Opcodes.ILOAD, 0); // load the optimistic parameter - Label end = newLabel(); - cb.ifZCmp(Opcodes.IFEQ, end); // if (optimistic) { var methodParameters = cb.getArgumentTypes(); - // Load all the arguments except the first one (the optimistic - // parameter) - var argTypes = copyOfRange(methodParameters, 1, methodParameters.length); - int index = Type.BOOLEAN_TYPE.getSize(); - for (var argType : argTypes) { - cb.visitVarInsn(argType.getOpcode(Opcodes.ILOAD), index); - index += argType.getSize(); + if (methodParameters.length > 0) { + cb.visitVarInsn(Opcodes.ILOAD, 0); // load the optimistic parameter + Label end = newLabel(); + cb.ifZCmp(Opcodes.IFEQ, end); // if (optimistic) { + var argTypes = copyOfRange(methodParameters, 1, methodParameters.length); + int index = Type.BOOLEAN_TYPE.getSize(); + for (var argType : argTypes) { + cb.visitVarInsn(argType.getOpcode(Opcodes.ILOAD), index); + index += argType.getSize(); + } + cb.visitInvokeDynamicInsn( + cb.getThisMethodName(), + Type.getMethodDescriptor(providerSubclassType, argTypes), + bootstrapHandle); + cb.returnValue(); + cb.mark(end); } - cb.visitInvokeDynamicInsn( - cb.getThisMethodName(), - Type.getMethodDescriptor(providerSubclassType, argTypes), - bootstrapHandle); - cb.returnValue(); - cb.mark(end); } }; return new ExpressionDetacher.BasicDetacher(Suppliers.ofInstance(optimisticDetacher)); diff --git a/java/src/com/google/template/soy/jbcsrc/restricted/CodeBuilder.java b/java/src/com/google/template/soy/jbcsrc/restricted/CodeBuilder.java index 5de788bcb9..17801b946d 100644 --- a/java/src/com/google/template/soy/jbcsrc/restricted/CodeBuilder.java +++ b/java/src/com/google/template/soy/jbcsrc/restricted/CodeBuilder.java @@ -153,6 +153,10 @@ public Type[] getArgumentTypes() { return adapter.getArgumentTypes(); } + public Type getReturnType() { + return adapter.getReturnType(); + } + /** See {@link GeneratorAdapter#pop()} */ public void pop() { adapter.pop();