2020-03-14 19:21:37 +01:00
|
|
|
/*
|
2021-05-23 12:42:37 +02:00
|
|
|
Copyright 2020 - 2021 Volker Berlin (i-net software)
|
2020-03-14 19:21:37 +01:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
2020-09-26 18:43:41 +02:00
|
|
|
package de.inetsoftware.jwebassembly.module.nativecode;
|
|
|
|
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.BOOLEAN;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.BYTE;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.CHAR;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.DOUBLE;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.FLOAT;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.INT;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.LONG;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.SHORT;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.TYPE_DESCRIPTION_ARRAY_TYPE;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.TYPE_DESCRIPTION_INSTANCEOF_OFFSET;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.TYPE_DESCRIPTION_TYPE_NAME;
|
|
|
|
import static de.inetsoftware.jwebassembly.module.TypeManager.VOID;
|
2020-03-14 19:21:37 +01:00
|
|
|
|
2021-05-02 14:47:39 +02:00
|
|
|
import java.lang.reflect.Method;
|
2021-05-23 12:04:20 +02:00
|
|
|
import java.lang.reflect.Type;
|
2021-05-02 14:47:39 +02:00
|
|
|
|
2020-03-14 19:21:37 +01:00
|
|
|
import de.inetsoftware.jwebassembly.api.annotation.Replace;
|
|
|
|
import de.inetsoftware.jwebassembly.api.annotation.WasmTextCode;
|
2020-09-26 18:43:41 +02:00
|
|
|
import de.inetsoftware.jwebassembly.module.TypeManager;
|
2020-09-20 13:59:13 +02:00
|
|
|
|
2020-03-14 19:21:37 +01:00
|
|
|
/**
|
|
|
|
* Replacement for java.lang.Class
|
|
|
|
*
|
2021-05-23 12:42:37 +02:00
|
|
|
* @param <T> the type of the class modeled by this {@code Class}
|
2020-03-14 19:21:37 +01:00
|
|
|
* @author Volker Berlin
|
|
|
|
*/
|
|
|
|
@Replace( "java/lang/Class" )
|
2021-05-23 11:58:35 +02:00
|
|
|
class ReplacementForClass<T> {
|
2020-03-14 19:21:37 +01:00
|
|
|
|
2020-09-27 13:17:58 +02:00
|
|
|
/**
|
|
|
|
* The pointer in the memory for the class/type description.
|
|
|
|
*/
|
|
|
|
final int vtable;
|
2020-03-14 19:21:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a instance
|
|
|
|
*
|
|
|
|
* @param vtable
|
|
|
|
* the pointer in the memory for the class/type description.
|
|
|
|
*/
|
|
|
|
private ReplacementForClass( int vtable ) {
|
|
|
|
this.vtable = vtable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-15 11:08:55 +01:00
|
|
|
* Replacement for {@link Object#getClass()}. The documentation of the memory of the type description is done in method:
|
|
|
|
* {@link TypeManager.StructType#writeToStream(java.io.ByteArrayOutputStream, java.util.function.ToIntFunction)}
|
2020-03-14 19:21:37 +01:00
|
|
|
*
|
|
|
|
* @param obj
|
|
|
|
* the instance
|
|
|
|
* @return the class
|
|
|
|
*/
|
|
|
|
@WasmTextCode( "local.get 0 " // THIS
|
|
|
|
+ "struct.get java/lang/Object .vtable " // vtable is on index 0
|
2020-03-14 23:02:58 +01:00
|
|
|
+ "local.tee 1 " // save the vtable location
|
2020-09-20 13:59:13 +02:00
|
|
|
+ "i32.const " + TYPE_DESCRIPTION_INSTANCEOF_OFFSET + " " // vtable is on index 0
|
2020-03-14 23:02:58 +01:00
|
|
|
+ "i32.add " //
|
|
|
|
+ "call $java/lang/Class.getIntFromMemory(I)I " //
|
|
|
|
+ "local.get 1 " // get the vtable location
|
|
|
|
+ "i32.add " //
|
2020-03-15 11:08:55 +01:00
|
|
|
+ "i32.const 4 " // length of instanceof
|
|
|
|
+ "i32.add " //
|
|
|
|
+ "call $java/lang/Class.getIntFromMemory(I)I " // first entry in instanceof is ever the id of the Class self
|
2020-04-04 11:18:25 +02:00
|
|
|
+ "call $java/lang/Class.classConstant(I)Ljava/lang/Class; " //
|
2020-03-14 19:21:37 +01:00
|
|
|
+ "return " //
|
|
|
|
)
|
|
|
|
@Replace( "java/lang/Object.getClass()Ljava/lang/Class;" )
|
|
|
|
private static native ReplacementForClass getClassObject( Object obj );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WASM code
|
|
|
|
* <p>
|
|
|
|
* Get a constant Class from the table.
|
|
|
|
*
|
|
|
|
* @param classIdx
|
|
|
|
* the id/index of the Class.
|
|
|
|
* @return the string
|
2020-03-15 15:49:52 +01:00
|
|
|
* @see TypeManager#getClassConstantFunction()
|
2020-03-14 19:21:37 +01:00
|
|
|
*/
|
|
|
|
private static ReplacementForClass classConstant( int classIdx ) {
|
|
|
|
ReplacementForClass clazz = getClassFromTable( classIdx );
|
|
|
|
if( clazz != null ) {
|
|
|
|
return clazz;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
The memory/data section has the follow content:
|
|
|
|
┌──────────────────────────────────┐
|
|
|
|
| Type/Class descriptions (vtable) |
|
|
|
|
├──────────────────────────────────┤
|
|
|
|
| Type/Class table |
|
|
|
|
├──────────────────────────────────┤
|
|
|
|
| String table |
|
|
|
|
└──────────────────────────────────┘
|
|
|
|
*/
|
|
|
|
int vtable = getIntFromMemory( classIdx * 4 + typeTableMemoryOffset() );
|
|
|
|
clazz = new ReplacementForClass( vtable );
|
|
|
|
// save the string for future use
|
|
|
|
setClassIntoTable( classIdx, clazz );
|
|
|
|
return clazz;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WASM code
|
|
|
|
* <p>
|
|
|
|
* Get a Class instance from the Class table. Should be inlined from the optimizer.
|
|
|
|
*
|
|
|
|
* @param classIdx
|
|
|
|
* the id/index of the Class.
|
|
|
|
* @return the string or null if not already set.
|
|
|
|
*/
|
|
|
|
@WasmTextCode( "local.get 0 " + //
|
|
|
|
"table.get 2 " + // table 2 is used for classes
|
|
|
|
"return" )
|
|
|
|
private static native ReplacementForClass getClassFromTable( int classIdx );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WASM code
|
|
|
|
* <p>
|
2020-03-15 11:08:55 +01:00
|
|
|
* Set a Class instance in the Class table. Should be inlined from the optimizer.
|
2020-03-14 19:21:37 +01:00
|
|
|
*
|
|
|
|
* @param strIdx
|
|
|
|
* the id/index of the string.
|
2020-03-15 11:08:55 +01:00
|
|
|
* @param clazz
|
|
|
|
* the Class instance
|
2020-03-14 19:21:37 +01:00
|
|
|
*/
|
|
|
|
@WasmTextCode( "local.get 0 " + //
|
|
|
|
"local.get 1 " + //
|
2020-03-14 23:02:58 +01:00
|
|
|
"table.set 2 " + // table 2 is used for classes
|
2020-03-14 19:21:37 +01:00
|
|
|
"return" )
|
|
|
|
private static native void setClassIntoTable( int strIdx, ReplacementForClass clazz );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WASM code
|
|
|
|
* <p>
|
|
|
|
* Placeholder for a synthetic function. Should be inlined from the optimizer.
|
|
|
|
*
|
|
|
|
* @return the memory offset of the string data in the element section
|
|
|
|
*/
|
|
|
|
private static native int typeTableMemoryOffset();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* WASM code
|
|
|
|
* <p>
|
|
|
|
* Load an i32 from memory. The offset must be aligned. Should be inlined from the optimizer.
|
|
|
|
*
|
|
|
|
* @param pos
|
|
|
|
* the memory position
|
|
|
|
* @return the value from the memory
|
|
|
|
*/
|
|
|
|
@WasmTextCode( "local.get 0 " + //
|
|
|
|
"i32.load offset=0 align=4 " + //
|
|
|
|
"return" )
|
|
|
|
private static native int getIntFromMemory( int pos );
|
2020-04-04 16:09:58 +02:00
|
|
|
|
2021-05-30 10:52:57 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods forName(String)
|
|
|
|
*
|
|
|
|
* @param className
|
|
|
|
* the fully qualified name of the desired class.
|
|
|
|
* @return the {@code Class} object for the class with the specified name.
|
|
|
|
*/
|
|
|
|
public static Class<?> forName( String className ) throws ClassNotFoundException {
|
|
|
|
throw new ClassNotFoundException( className ); // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replacement of the Java methods newInstance()
|
|
|
|
*
|
|
|
|
* @return a newly allocated instance of the class represented by this object.
|
|
|
|
*/
|
|
|
|
public T newInstance() throws InstantiationException, IllegalAccessException {
|
|
|
|
throw new InstantiationException( getName() ); // TODO
|
|
|
|
}
|
|
|
|
|
2021-05-23 14:36:18 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods isInstance()
|
|
|
|
*
|
|
|
|
* @param obj
|
|
|
|
* the object to check
|
|
|
|
* @return true if {@code obj} is an instance of this class
|
|
|
|
*/
|
|
|
|
@WasmTextCode( "unreachable" ) // TODO
|
|
|
|
public native boolean isInstance( Object obj );
|
|
|
|
|
2021-05-23 12:42:37 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods isArray()
|
|
|
|
* @return {@code true} if this object represents an array class;
|
|
|
|
*/
|
|
|
|
public boolean isArray() {
|
|
|
|
int classIdx = getIntFromMemory( vtable + TYPE_DESCRIPTION_ARRAY_TYPE );
|
|
|
|
return classIdx >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replacement for {@link Class#getName()}
|
|
|
|
*
|
|
|
|
* @return the name
|
|
|
|
*/
|
|
|
|
public String getName() {
|
|
|
|
return StringTable.stringConstant( getIntFromMemory( vtable + TYPE_DESCRIPTION_TYPE_NAME ) );
|
|
|
|
}
|
|
|
|
|
2020-05-15 18:27:46 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods
|
|
|
|
*/
|
2021-05-23 11:58:35 +02:00
|
|
|
public ClassLoader getClassLoader() {
|
2020-05-15 18:27:46 +02:00
|
|
|
return null;
|
|
|
|
}
|
2020-04-04 16:09:58 +02:00
|
|
|
|
2020-05-21 16:28:52 +02:00
|
|
|
/**
|
2021-05-23 12:42:37 +02:00
|
|
|
* Replacement of the Java methods getClassLoader0()
|
2020-05-21 16:28:52 +02:00
|
|
|
*/
|
|
|
|
ClassLoader getClassLoader0() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-05-02 14:47:39 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods getSuperclass()
|
|
|
|
*/
|
|
|
|
@WasmTextCode( "unreachable" ) // TODO
|
2021-05-23 11:58:35 +02:00
|
|
|
public native Class<? super T> getSuperclass();
|
2021-05-02 14:47:39 +02:00
|
|
|
|
2021-05-23 12:04:20 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods getInterfaces()
|
|
|
|
* @return an array of interfaces implemented by this class.
|
|
|
|
*/
|
|
|
|
public Class<?>[] getInterfaces() { //TODO
|
|
|
|
return new Class<?>[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replacement of the Java methods getGenericInterfaces()
|
|
|
|
* @return an array of interfaces implemented by this class
|
|
|
|
*/
|
|
|
|
public Type[] getGenericInterfaces() { // TODO
|
|
|
|
return getInterfaces();
|
|
|
|
}
|
|
|
|
|
2020-05-31 11:39:57 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the native Java methods getComponentType()
|
|
|
|
*/
|
2021-05-23 11:58:35 +02:00
|
|
|
public ReplacementForClass<?> getComponentType() {
|
2020-09-20 13:59:13 +02:00
|
|
|
int classIdx = getIntFromMemory( vtable + TYPE_DESCRIPTION_ARRAY_TYPE );
|
2020-05-31 11:39:57 +02:00
|
|
|
return classIdx >= 0 ? classConstant( classIdx ) : null;
|
|
|
|
}
|
|
|
|
|
2021-05-23 14:36:18 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods getSimpleName()
|
|
|
|
*
|
|
|
|
* @return the simple name of the underlying class
|
|
|
|
*/
|
|
|
|
public String getSimpleName() {
|
|
|
|
if( isArray() )
|
|
|
|
return getComponentType().getSimpleName() + "[]";
|
|
|
|
|
|
|
|
String simpleName = getName();
|
|
|
|
int index = simpleName.lastIndexOf( "$" ) + 1;
|
|
|
|
if( index == 0 ) { // top level class
|
|
|
|
return simpleName.substring( simpleName.lastIndexOf( "." ) + 1 ); // strip the package name
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove leading "\$[0-9]*" from the name
|
|
|
|
int length = simpleName.length();
|
|
|
|
while( index < length ) {
|
|
|
|
char ch = simpleName.charAt( index );
|
|
|
|
if( '0' <= ch && ch <= '9' ) {
|
|
|
|
index++;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Eventually, this is the empty string iff this is an anonymous class
|
|
|
|
return simpleName.substring( index );
|
|
|
|
}
|
|
|
|
|
2021-05-02 14:47:39 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the Java methods getDeclaredMethod()
|
|
|
|
*/
|
|
|
|
@WasmTextCode( "unreachable" ) // TODO
|
2021-05-23 11:58:35 +02:00
|
|
|
public native Method getDeclaredMethod(String name, Class<?>... parameterTypes);
|
2021-05-02 14:47:39 +02:00
|
|
|
|
2020-04-04 16:09:58 +02:00
|
|
|
/**
|
|
|
|
* Replacement of the native Java methods
|
|
|
|
*
|
|
|
|
* @param name
|
|
|
|
* the class name
|
|
|
|
* @return the class
|
|
|
|
* @see TypeManager#PRIMITIVE_CLASSES
|
|
|
|
*/
|
2021-05-23 11:58:35 +02:00
|
|
|
static ReplacementForClass<?> getPrimitiveClass( String name ) {
|
2020-04-04 16:09:58 +02:00
|
|
|
switch( name ) {
|
|
|
|
case "boolean":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( BOOLEAN );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "byte":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( BYTE );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "char":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( CHAR );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "double":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( DOUBLE );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "float":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( FLOAT );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "int":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( INT );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "long":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( LONG );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "short":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( SHORT );
|
2020-04-04 16:09:58 +02:00
|
|
|
case "void":
|
2020-09-20 13:59:13 +02:00
|
|
|
return classConstant( VOID );
|
2020-04-04 16:09:58 +02:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replacement of the native Java methods.
|
|
|
|
*/
|
2021-05-23 11:58:35 +02:00
|
|
|
public boolean desiredAssertionStatus() {
|
2020-04-04 16:09:58 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-14 19:21:37 +01:00
|
|
|
}
|