From 8379416cb88957e664b7096efbe3e22eed5fb37b Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 9 Feb 2020 18:05:31 +0100 Subject: [PATCH] add support for multi dimension array allocation --- .../JavaScriptNewMultiArrayFunctionName.java | 163 ++++++++++++++++++ .../module/JavaMethodWasmCodeBuilder.java | 6 +- .../jwebassembly/module/WasmCodeBuilder.java | 24 +++ .../runtime/MultiArrayOperations.java | 67 +++++++ 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 src/de/inetsoftware/jwebassembly/javascript/JavaScriptNewMultiArrayFunctionName.java create mode 100644 test/de/inetsoftware/jwebassembly/runtime/MultiArrayOperations.java diff --git a/src/de/inetsoftware/jwebassembly/javascript/JavaScriptNewMultiArrayFunctionName.java b/src/de/inetsoftware/jwebassembly/javascript/JavaScriptNewMultiArrayFunctionName.java new file mode 100644 index 0000000..6040af4 --- /dev/null +++ b/src/de/inetsoftware/jwebassembly/javascript/JavaScriptNewMultiArrayFunctionName.java @@ -0,0 +1,163 @@ +package de.inetsoftware.jwebassembly.javascript; + +import java.util.Arrays; +/* +Copyright 2020 Volker Berlin (i-net software) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ +import java.util.function.Supplier; + +import de.inetsoftware.jwebassembly.wasm.AnyType; +import de.inetsoftware.jwebassembly.wasm.ArrayType; +import de.inetsoftware.jwebassembly.wasm.ValueType; + +/** + * Synthetic functions for creating multidimensional dimensional arrays + * + * @author Volker Berlin + */ +public class JavaScriptNewMultiArrayFunctionName extends JavaScriptSyntheticFunctionName { + + /** + * Create a new instance + * + * @param dim + * the count of dimensions, should be >= 2 + * @param type + * the full type of the allocated array + */ + public JavaScriptNewMultiArrayFunctionName( int dim, ArrayType type ) { + super( "NonGC", createName( dim, type ), createJS( dim, type ), createSignature( dim, type ) ); + } + + /** + * The element type of the array + * + * @param type + * the full type of the allocated array + * @return the element type + */ + private static ValueType getElementType( ArrayType type ) { + do { + AnyType arrayType = type.getArrayType(); + if( arrayType.getClass() != ArrayType.class ) { + type = (ArrayType)arrayType; + continue; + } + return arrayType.getClass() == ValueType.class ? (ValueType)arrayType : ValueType.anyref; + } while( true ); + } + + /** + * Create the unique name depends on dimension and type + * + * @param dim + * the dimension + * @param type + * the full type of the allocated array + * @return the name + */ + private static String createName( int dim, ArrayType type ) { + return "array_newmulti" + dim + "_" + getElementType( type ); + } + + /** + * Create the signature of the function. + * + * @param dim + * the dimension + * @param type + * the full type of the allocated array + * @return the signature + */ + private static AnyType[] createSignature( int dim, ArrayType type ) { + AnyType[] signature = new AnyType[dim + 2]; + Arrays.fill( signature, 0, dim, ValueType.i32 ); + signature[dim + 1] = type; + return signature; + } + + /** + * Get the factory for the JavaScript method + * + * @param dim + * the dimension + * @param type + * the full type of the allocated array + * @return the JavaScript factory + */ + private static Supplier createJS( int dim, ArrayType type ) { + return () -> { + // the dimention that must be array and not typed array + int dimMulti = dim - 1; + + // create parameter signature + StringBuilder js = new StringBuilder( "(" ); + for( int i = 0; i < dimMulti; i++ ) { + js.append( 'd' ).append( i ); + js.append( ',' ); + } + js.append( "l)=>" ); + + createJsArray( js, 0, dimMulti, type ); + + return js.toString(); + }; + } + + /** + * Recursion for the allocation with default value + * + * @param js + * the target + * @param idx + * running dimension index + * @param dimMulti + * the count of not typed dimensions + * @param type + * the full type of the allocated array + */ + private static void createJsArray( StringBuilder js, int idx, int dimMulti, ArrayType type ) { + js.append( "Array.from({length:d" ).append( idx ).append( "}, ()=>" ); + idx++; + if( idx < dimMulti ) { + createJsArray( js, idx, dimMulti, type ); + } else { + switch( getElementType( type ) ) { + case i8: + js.append( "new Uint8Array(l)" ); + break; + case i16: + js.append( "new Int16Array(l)" ); + break; + case i32: + js.append( "new Int32Array(l)" ); + break; + case i64: + js.append( "new BigInt64Array(l)" ); + break; + case f32: + js.append( "new Float32Array(l)" ); + break; + case f64: + js.append( "new Float64Array(l)" ); + break; + default: + js.append( "Object.seal(new Array(l).fill(null))" ); + } + } + js.append( ')' ); + } +} diff --git a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java index f85632a..f9a4644 100644 --- a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java @@ -670,7 +670,11 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.wide wide = true; continue; - //TODO case 197: // multianewarray + case 197: // multianewarray + name = ((ConstantClass)constantPool.get( byteCode.readUnsignedShort() )).getName(); + idx = byteCode.readUnsignedByte(); + addMultiNewArrayInstruction( idx, name, codePos, lineNumber ); + break; case 198: // ifnull opIfCompareCondition( NumericOperator.ifnull, byteCode, codePos, lineNumber ); break; diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index 363db25..27eebce 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -29,11 +29,13 @@ import de.inetsoftware.classparser.LocalVariableTable; import de.inetsoftware.classparser.Member; import de.inetsoftware.classparser.MethodInfo; import de.inetsoftware.jwebassembly.WasmException; +import de.inetsoftware.jwebassembly.javascript.JavaScriptNewMultiArrayFunctionName; import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName; import de.inetsoftware.jwebassembly.javascript.NonGC; import de.inetsoftware.jwebassembly.module.WasmInstruction.Type; import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.ArrayOperator; +import de.inetsoftware.jwebassembly.wasm.ArrayType; import de.inetsoftware.jwebassembly.wasm.MemoryOperator; import de.inetsoftware.jwebassembly.wasm.NamedStorageType; import de.inetsoftware.jwebassembly.wasm.NumericOperator; @@ -591,6 +593,28 @@ public abstract class WasmCodeBuilder { } } + /** + * Add a new multi dimensional array instruction + * + * @param dim + * the dimension of the array >= 2 + * @param typeName + * the full type name + * @param javaCodePos + * the code position/offset in the Java method + * @param lineNumber + * the line number in the Java source code + */ + protected void addMultiNewArrayInstruction( int dim, String typeName, int javaCodePos, int lineNumber ) { + ArrayType type = (ArrayType)new ValueTypeParser( typeName, types ).next(); + if( options.useGC() ) { + throw new WasmException( "multi new array is not supported", lineNumber ); + } else { + FunctionName name = new JavaScriptNewMultiArrayFunctionName( dim, type ); + addCallInstruction( name, javaCodePos, lineNumber ); + } + } + /** * Add an array operation to the instruction list as marker on the code position. * diff --git a/test/de/inetsoftware/jwebassembly/runtime/MultiArrayOperations.java b/test/de/inetsoftware/jwebassembly/runtime/MultiArrayOperations.java new file mode 100644 index 0000000..8ce1713 --- /dev/null +++ b/test/de/inetsoftware/jwebassembly/runtime/MultiArrayOperations.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Volker Berlin (i-net software) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.inetsoftware.jwebassembly.runtime; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.zip.CRC32; + +import org.junit.Assume; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameters; + +import de.inetsoftware.jwebassembly.ScriptEngine; +import de.inetsoftware.jwebassembly.WasmRule; +import de.inetsoftware.jwebassembly.api.annotation.Export; + +public class MultiArrayOperations extends AbstractBaseTest { + + @ClassRule + public static WasmRule rule = new WasmRule( TestClass.class ); + + public MultiArrayOperations( ScriptEngine script, String method, Object[] params ) { + super( rule, script, method, params ); + } + + @Parameters( name = "{0}-{1}" ) + public static Collection data() { + ArrayList list = new ArrayList<>(); + + ScriptEngine[] engines = ScriptEngine.testEngines(); + for( ScriptEngine script : engines ) { + addParam( list, script, "multi" ); + } + rule.setTestParameters( list ); + return list; + } + + @Test + @Override + public void test() { + super.test(); + } + + static class TestClass { + + @Export + static int multi() { + int[][][] val = new int[1][2][3]; + return val.length; + } + } +}