From df2f4cec69a6d0cf0c6eb2f2b5fcabd4ea4823f3 Mon Sep 17 00:00:00 2001 From: Stan Hebben Date: Fri, 15 Mar 2024 21:16:42 +0100 Subject: [PATCH] Fix boxing of generics in generic method and constructor calls + fix constructor calls on generic classes --- .../compiler/JavaMethodBytecodeCompiler.java | 40 ++++++++----- .../javashared/JavaBuiltinModule.java | 8 +-- .../javashared/JavaNativeMethod.java | 35 +++++++++++ .../zencode-tests/generics/generic-boxing.zc | 20 +++++++ .../zencode-tests/maps/boxed-value-test.zc | 12 ++++ .../java_native/AutoBoxingTest.java | 58 ------------------- 6 files changed, 97 insertions(+), 76 deletions(-) create mode 100644 ScriptingEngineTester/src/main/resources/zencode-tests/generics/generic-boxing.zc create mode 100644 ScriptingEngineTester/src/main/resources/zencode-tests/maps/boxed-value-test.zc delete mode 100644 ScriptingExample/src/test/java/org/openzen/zenscript/scriptingexample/tests/actual_test/java_native/AutoBoxingTest.java diff --git a/JavaBytecodeCompiler/src/main/java/org/openzen/zenscript/javabytecode/compiler/JavaMethodBytecodeCompiler.java b/JavaBytecodeCompiler/src/main/java/org/openzen/zenscript/javabytecode/compiler/JavaMethodBytecodeCompiler.java index 3fb666679..c3d46d3cf 100644 --- a/JavaBytecodeCompiler/src/main/java/org/openzen/zenscript/javabytecode/compiler/JavaMethodBytecodeCompiler.java +++ b/JavaBytecodeCompiler/src/main/java/org/openzen/zenscript/javabytecode/compiler/JavaMethodBytecodeCompiler.java @@ -11,12 +11,13 @@ import org.openzen.zenscript.javashared.*; import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; public class JavaMethodBytecodeCompiler implements JavaMethodCompiler { private static final JavaNativeMethod BOOLEAN_TO_STRING = JavaNativeMethod.getNativeStatic(JavaClass.BOOLEAN, "toString", "(Z)Ljava/long/String;"); public static final JavaNativeMethod OBJECT_HASHCODE = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "hashCode", "()I"); - public static final JavaNativeMethod OBJECT_EQUALS = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "equals", "(Ljava/lang/Object)Z"); + public static final JavaNativeMethod OBJECT_EQUALS = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "equals", "(Ljava/lang/Object;)Z"); public static final JavaNativeMethod OBJECT_CLONE = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "clone", "()Ljava/lang/Object;"); private static final JavaNativeMethod OBJECTS_TOSTRING = JavaNativeMethod.getNativeStatic(new JavaClass("java.util", "Objects", JavaClass.Kind.CLASS), "toString", "(Ljava/lang/Object;)Ljava/lang/String;"); private static final JavaNativeMethod BYTE_PARSE = JavaNativeMethod.getNativeStatic(JavaClass.BYTE, "parseByte", "(Ljava/lang/String;)B"); @@ -102,12 +103,12 @@ public class JavaMethodBytecodeCompiler implements JavaMethodCompiler { private static final JavaNativeMethod ARRAYS_COPY_OF_RANGE_FLOATS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "copyOfRange", "([FII)[F"); private static final JavaNativeMethod ARRAYS_COPY_OF_RANGE_DOUBLES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "copyOfRange", "([DII)[D"); private static final JavaNativeMethod ARRAYS_COPY_OF_RANGE_CHARS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "copyOfRange", "([CII)[C"); - private static final JavaNativeMethod ARRAYS_EQUALS_OBJECTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([Ljava/lang/Object[Ljava/lang/Object)Z"); + private static final JavaNativeMethod ARRAYS_EQUALS_OBJECTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([Ljava/lang/Object;[Ljava/lang/Object;)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_BOOLS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([Z[Z)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_BYTES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([B[B)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_SHORTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([S[S)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_INTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([I[I)Z"); - private static final JavaNativeMethod ARRAYS_EQUALS_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([L[L)Z"); + private static final JavaNativeMethod ARRAYS_EQUALS_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([J[J)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_FLOATS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([F[F)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_DOUBLES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([D[D)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_CHARS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([C[C)Z"); @@ -116,7 +117,7 @@ public class JavaMethodBytecodeCompiler implements JavaMethodCompiler { private static final JavaNativeMethod ARRAYS_HASHCODE_BYTES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([B)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_SHORTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([S)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_INTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([I)I"); - private static final JavaNativeMethod ARRAYS_HASHCODE_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([L)I"); + private static final JavaNativeMethod ARRAYS_HASHCODE_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([J)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_FLOATS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([F)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_DOUBLES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([D)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_CHARS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([C)I"); @@ -147,12 +148,17 @@ public JavaMethodBytecodeCompiler(JavaWriter javaWriter, JavaExpressionVisitor e public Void nativeConstructor(JavaNativeMethod method, TypeID type, CallArguments arguments) { javaWriter.newObject(method.cls); javaWriter.dup(); - for (Expression argument : arguments.arguments) { - argument.accept(expressionVisitor); - } + AtomicInteger typeArguments = new AtomicInteger(0); if (method.compile) { - handleTypeArguments(method, arguments); + type.asDefinition().ifPresent(definitionType -> { + final JavaTypeExpressionVisitor javaTypeExpressionVisitor = new JavaTypeExpressionVisitor(context); + typeArguments.set(definitionType.typeArguments.length); + for (TypeID typeParameter : definitionType.typeArguments) { + typeParameter.accept(javaWriter, javaTypeExpressionVisitor); + } + }); } + handleArguments(typeArguments.get(), method, arguments); javaWriter.invokeSpecial(method); return null; } @@ -165,9 +171,7 @@ public Void nativeVirtualMethod(JavaNativeMethod method, TypeID returnType, Expr @Override public Void nativeStaticMethod(JavaNativeMethod method, TypeID returnType, CallArguments arguments) { - for (Expression argument : arguments.arguments) { - argument.accept(expressionVisitor); - } + handleArguments(arguments.typeArguments.length, method, arguments); if (method.compile) { handleTypeArguments(method, arguments); } @@ -200,9 +204,7 @@ public Void nativeStaticMethod(JavaNativeMethod method, TypeID returnType, CallA @Override public Void nativeSpecialMethod(JavaNativeMethod method, TypeID returnType, Expression target, CallArguments arguments) { target.accept(expressionVisitor); - for (Expression argument : arguments.arguments) { - argument.accept(expressionVisitor); - } + handleArguments(arguments.typeArguments.length, method, arguments); if (method.compile) { handleTypeArguments(method, arguments); } @@ -210,6 +212,16 @@ public Void nativeSpecialMethod(JavaNativeMethod method, TypeID returnType, Expr return null; } + private void handleArguments(int typeArguments, JavaNativeMethod method, CallArguments arguments) { + for (int index = 0; index < arguments.arguments.length; index++) { + Expression argument = arguments.arguments[index]; + argument.accept(expressionVisitor); + if (!method.primitiveArguments[typeArguments + index]) { + argument.type.accept(argument.type, boxingTypeVisitor); + } + } + } + private void handleTypeArguments(JavaNativeMethod method, CallArguments arguments) { final JavaTypeExpressionVisitor javaTypeExpressionVisitor = new JavaTypeExpressionVisitor(context); if (arguments.typeArguments.length != method.typeParameterArguments.length) diff --git a/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaBuiltinModule.java b/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaBuiltinModule.java index f9561d71b..3ab49cdeb 100644 --- a/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaBuiltinModule.java +++ b/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaBuiltinModule.java @@ -6,7 +6,7 @@ public class JavaBuiltinModule { public static final JavaNativeMethod OBJECT_HASHCODE = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "hashCode", "()I"); - public static final JavaNativeMethod OBJECT_EQUALS = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "equals", "(Ljava/lang/Object)Z"); + public static final JavaNativeMethod OBJECT_EQUALS = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "equals", "(Ljava/lang/Object;)Z"); public static final JavaNativeMethod OBJECT_CLONE = JavaNativeMethod.getNativeVirtual(JavaClass.OBJECT, "clone", "()Ljava/lang/Object;"); private static final JavaNativeMethod OBJECTS_TOSTRING = JavaNativeMethod.getNativeStatic(new JavaClass("java.util", "Objects", JavaClass.Kind.CLASS), "toString", "(Ljava/lang/Object;)Ljava/lang/String;"); private static final JavaNativeMethod BYTE_PARSE = JavaNativeMethod.getNativeStatic(JavaClass.BYTE, "parseByte", "(Ljava/lang/String;)B"); @@ -102,12 +102,12 @@ public class JavaBuiltinModule { private static final JavaNativeMethod ARRAYS_COPY_OF_RANGE_FLOATS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "copyOfRange", "([FII)[F"); private static final JavaNativeMethod ARRAYS_COPY_OF_RANGE_DOUBLES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "copyOfRange", "([DII)[D"); private static final JavaNativeMethod ARRAYS_COPY_OF_RANGE_CHARS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "copyOfRange", "([CII)[C"); - private static final JavaNativeMethod ARRAYS_EQUALS_OBJECTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([Ljava/lang/Object[Ljava/lang/Object)Z"); + private static final JavaNativeMethod ARRAYS_EQUALS_OBJECTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([Ljava/lang/Object;[Ljava/lang/Object;)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_BOOLS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([Z[Z)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_BYTES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([B[B)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_SHORTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([S[S)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_INTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([I[I)Z"); - private static final JavaNativeMethod ARRAYS_EQUALS_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([L[L)Z"); + private static final JavaNativeMethod ARRAYS_EQUALS_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([J[J)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_FLOATS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([F[F)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_DOUBLES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([D[D)Z"); private static final JavaNativeMethod ARRAYS_EQUALS_CHARS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "equals", "([C[C)Z"); @@ -116,7 +116,7 @@ public class JavaBuiltinModule { private static final JavaNativeMethod ARRAYS_HASHCODE_BYTES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([B)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_SHORTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([S)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_INTS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([I)I"); - private static final JavaNativeMethod ARRAYS_HASHCODE_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([L)I"); + private static final JavaNativeMethod ARRAYS_HASHCODE_LONGS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([J)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_FLOATS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([F)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_DOUBLES = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([D)I"); private static final JavaNativeMethod ARRAYS_HASHCODE_CHARS = JavaNativeMethod.getNativeStatic(JavaClass.ARRAYS, "hashCode", "([C)I"); diff --git a/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaNativeMethod.java b/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaNativeMethod.java index 231568efb..1343e6b77 100644 --- a/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaNativeMethod.java +++ b/JavaShared/src/main/java/org/openzen/zenscript/javashared/JavaNativeMethod.java @@ -9,6 +9,8 @@ import org.openzen.zenscript.codemodel.expression.Expression; import org.openzen.zenscript.codemodel.type.TypeID; +import java.util.Arrays; + /** * @author Hoofdgebruiker */ @@ -21,6 +23,7 @@ public class JavaNativeMethod implements JavaMethod { public final int modifiers; // these are Java modifiers! public final boolean genericResult; public final boolean[] typeParameterArguments; + public final boolean[] primitiveArguments; public JavaNativeMethod(JavaClass cls, Kind kind, String name, boolean compile, String descriptor, int modifiers, boolean genericResult) { this(cls, kind, name, compile, descriptor, modifiers, genericResult, new boolean[0]); @@ -41,6 +44,7 @@ public JavaNativeMethod(JavaClass cls, Kind kind, String name, boolean compile, this.modifiers = modifiers; this.genericResult = genericResult; this.typeParameterArguments = typeParameterArguments; + this.primitiveArguments = determinePrimitiveArguments(descriptor); } public static JavaNativeMethod getConstructor(JavaClass cls, String descriptor, int modifiers) { @@ -134,4 +138,35 @@ public enum Kind { CONSTRUCTOR, COMPILED } + + private static boolean[] determinePrimitiveArguments(String descriptor) { + try { + boolean[] result = new boolean[descriptor.length()]; + int index = 0; + for (int i = 1; i < descriptor.length(); i++) { + if (descriptor.charAt(i) == ')') + break; + + char c = descriptor.charAt(i); + if (c == 'L') { + while (descriptor.charAt(i) != ';') + i++; + result[index++] = false; + } else if (c == '[') { + while (descriptor.charAt(i) == '[') + i++; + if (descriptor.charAt(i) == 'L') { + while (descriptor.charAt(i) != ';') + i++; + } + result[index++] = false; + } else { + result[index++] = true; + } + } + return Arrays.copyOf(result, index); + } catch (StringIndexOutOfBoundsException ex) { + throw new IllegalArgumentException("Invalid descriptor: " + descriptor, ex); + } + } } diff --git a/ScriptingEngineTester/src/main/resources/zencode-tests/generics/generic-boxing.zc b/ScriptingEngineTester/src/main/resources/zencode-tests/generics/generic-boxing.zc new file mode 100644 index 000000000..a9c5fecdb --- /dev/null +++ b/ScriptingEngineTester/src/main/resources/zencode-tests/generics/generic-boxing.zc @@ -0,0 +1,20 @@ +#output: Jimmy +#output: 19 + +public class Duad { + public var a as A : get; + public var b as B : get; + public this(a as A, b as B) { + this.a = a; + this.b = b; + } +} + +public function duadExample(name as string, age as int) as Duad { + return new Duad(name, age); +} + +var jimmy = duadExample("Jimmy", 19); + +println(jimmy.a); +println(jimmy.b); diff --git a/ScriptingEngineTester/src/main/resources/zencode-tests/maps/boxed-value-test.zc b/ScriptingEngineTester/src/main/resources/zencode-tests/maps/boxed-value-test.zc new file mode 100644 index 000000000..de02197db --- /dev/null +++ b/ScriptingEngineTester/src/main/resources/zencode-tests/maps/boxed-value-test.zc @@ -0,0 +1,12 @@ +#output: 1 +#output: test + +var item_values = {} as int[string]; +item_values["box"] = 1; +item_values["test"] = 9; +println(item_values["box"]); + +var value_items = {} as string[int]; +value_items[1] = "box"; +value_items[9] = "test"; +println(value_items[9]); diff --git a/ScriptingExample/src/test/java/org/openzen/zenscript/scriptingexample/tests/actual_test/java_native/AutoBoxingTest.java b/ScriptingExample/src/test/java/org/openzen/zenscript/scriptingexample/tests/actual_test/java_native/AutoBoxingTest.java deleted file mode 100644 index 2b0c892d4..000000000 --- a/ScriptingExample/src/test/java/org/openzen/zenscript/scriptingexample/tests/actual_test/java_native/AutoBoxingTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.openzen.zenscript.scriptingexample.tests.actual_test.java_native; - -import org.junit.jupiter.api.Test; -import org.openzen.zenscript.scriptingexample.tests.helpers.ZenCodeTest; - -public class AutoBoxingTest extends ZenCodeTest { - @Test - public void testIndexSetCorrectlyBoxesKeysAndValues() { - addScript( - "var item_values = {} as int[string];\n" + - "item_values[\"box\"] = 1;\n" + - "item_values[\"test\"] = 9;\n" + - "println(item_values[\"box\"]);\n" + - "\n" + - "var value_items = {} as string[int];\n" + - "value_items[1] = \"box\";\n" + - "value_items[9] = \"test\";\n" + - "println(value_items[9]);" - ); - executeEngine(); - - logger.assertNoErrors(); - logger.assertNoWarnings(); - logger.assertPrintOutputSize(2); - logger.assertPrintOutput(0, Integer.toString(1)); - logger.assertPrintOutput(1, "test"); - } - - @Test - public void testGenericParameterBoxing() { - addScript( - "public class Duad {\n" + - " public var a as A : get;\n" + - " public var b as B : get;\n" + - " public this(a as A, b as B) {\n" + - " this.a = a;\n" + - " this.b = b;\n" + - " }\n" + - "}\n" + - "\n" + - "public function duadExample(name as string, age as int) as Duad {\n" + - " return new Duad(name, age);\n" + - "}\n" + - "\n" + - "var jimmy = duadExample(\"Jimmy\", 19);\n" + - "\n" + - "println(jimmy.a);" + - "println(jimmy.b);" - ); - executeEngine(); - - logger.assertNoErrors(); - logger.assertNoWarnings(); - logger.assertPrintOutputSize(2); - logger.assertPrintOutput(0, "Jimmy"); - logger.assertPrintOutput(1, "19"); - } -}