implementation of a a replacement for java.lang.Class

This commit is contained in:
Volker Berlin 2020-03-14 19:21:37 +01:00
parent a6f1743109
commit 946e911982
5 changed files with 223 additions and 8 deletions

View File

@ -472,7 +472,11 @@ public class ModuleGenerator {
javaCodeBuilder.buildCode( code, method );
return javaCodeBuilder;
} else {
throw new WasmException( "Abstract or native method can not be used: " + new FunctionName( method ).signatureName, -1 );
FunctionName name = new FunctionName( method );
if( "de/inetsoftware/jwebassembly/module/ReplacementForClass.typeTableMemoryOffset()I".equals( name.signatureName ) ) {
return types.getTypeTableMemoryOffsetFunctionName().getCodeBuilder( watParser );
}
throw new WasmException( "Abstract or native method can not be used: " + name.signatureName, -1 );
}
} catch( Exception ioex ) {
int lineNumber = code == null ? -1 : code.getFirstLineNr();
@ -533,7 +537,7 @@ public class ModuleGenerator {
StructType structType = instr.getStructType();
List<NamedStorageType> list = structType.getFields();
for( NamedStorageType storageType : list ) {
if( TypeManager.VTABLE == storageType.getName() ) {
if( TypeManager.FIELD_VTABLE == storageType.getName() ) {
writer.writeConst( structType.getVTable(), ValueType.i32 );
} else {
writer.writeDefaultValue( storageType.getType() );

View File

@ -0,0 +1,151 @@
/*
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.module;
import de.inetsoftware.jwebassembly.api.annotation.Replace;
import de.inetsoftware.jwebassembly.api.annotation.WasmTextCode;
/**
* Replacement for java.lang.Class
*
* @author Volker Berlin
*/
@Replace( "java/lang/Class" )
class ReplacementForClass {
private final int vtable;
/**
* Create a instance
*
* @param vtable
* the pointer in the memory for the class/type description.
*/
private ReplacementForClass( int vtable ) {
this.vtable = vtable;
}
/**
* Replacement for {@link Class#getName()}
*
* @return the name
*/
String getName() {
return StringManager.stringConstant( getIntFromMemory( vtable + TypeManager.TYPE_DESCRIPTION_TYPE_NAME ) );
}
/**
* Replacement for {@link Object#getClass()}
*
* @param obj
* the instance
* @return the class
*/
@WasmTextCode( "local.get 0 " // THIS
+ "struct.get java/lang/Object .vtable " // vtable is on index 0
+ "call $de/inetsoftware/jwebassembly/module/ReplacementForClass.classConstant(I)Lde/inetsoftware/jwebassembly/module/ReplacementForClass; " //
+ "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
* @see #CLASS_CONSTANT_FUNCTION
*/
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>
* Set a string from the string table. Should be inlined from the optimizer.
*
* @param strIdx
* the id/index of the string.
* @param str
* the string
*/
@WasmTextCode( "local.get 0 " + //
"local.get 1 " + //
"table.set 2 " + //
"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 );
}

View File

@ -190,7 +190,7 @@ public class StringManager extends LinkedHashMap<String, Integer> {
* @return the string
* @see #STRING_CONSTANT_FUNCTION
*/
private static String stringConstant( int strIdx ) {
static String stringConstant( int strIdx ) {
String str = getStringFromTable( strIdx );
if( str != null ) {
return str;

View File

@ -50,12 +50,27 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
public class TypeManager {
/** name of virtual function table, start with a point for an invalid Java identifier */
static final String VTABLE = ".vtable";
static final String FIELD_VTABLE = ".vtable";
/**
* Name of field with system hash code, start with a point for an invalid Java identifier.
*/
static final String HASHCODE = ".hashcode";
static final String FIELD_HASHCODE = ".hashcode";
/**
* Byte position in the type description that contains the offset to the interfaces. Length 4 bytes.
*/
static final int TYPE_DESCRIPTION_INTERFACE_OFFSET = 0;
/**
* Byte position in the type description that contains the offset to the instanceof list. Length 4 bytes.
*/
static final int TYPE_DESCRIPTION_INSTANCEOF_OFFSET = 4;
/**
* Byte position in the type description that contains the offset to class name idx. Length 4 bytes.
*/
static final int TYPE_DESCRIPTION_TYPE_NAME = 8;
/**
* The reserved position on start of the vtable:
@ -72,6 +87,8 @@ public class TypeManager {
private final WasmOptions options;
private int typeTableOffset;
/**
* Initialize the type manager.
*
@ -108,6 +125,29 @@ public class TypeManager {
for( StructType type : structTypes.values() ) {
type.writeStructType( writer, functions, this, classFileLoader );
}
// write type table
ByteArrayOutputStream dataStream = writer.dataStream;
typeTableOffset = dataStream.size();
for( StructType type : structTypes.values() ) {
dataStream.write( type.vtableOffset );
}
}
/**
* Create an accessor for typeTableOffset and mark it.
*
* @return the function name
*/
WatCodeSyntheticFunctionName getTypeTableMemoryOffsetFunctionName() {
WatCodeSyntheticFunctionName offsetFunction =
new WatCodeSyntheticFunctionName( "de/inetsoftware/jwebassembly/module/ReplacementForClass", "typeTableMemoryOffset", "()I", "", null, ValueType.i32 ) {
protected String getCode() {
return "i32.const " + typeTableOffset;
}
};
options.functions.markAsNeeded( offsetFunction );
return offsetFunction;
}
/**
@ -352,8 +392,8 @@ public class TypeManager {
String superClassName = superClass.getName();
listStructFields( superClassName, functions, types, classFileLoader, allNeededFields );
} else {
fields.add( new NamedStorageType( ValueType.i32, className, VTABLE ) );
fields.add( new NamedStorageType( ValueType.i32, className, HASHCODE ) );
fields.add( new NamedStorageType( ValueType.i32, className, FIELD_VTABLE ) );
fields.add( new NamedStorageType( ValueType.i32, className, FIELD_HASHCODE ) );
}
for( FieldInfo field : classFile.getFields() ) {
@ -443,6 +483,23 @@ public class TypeManager {
* should never occur
*/
public void writeToStream( ByteArrayOutputStream dataStream, ToIntFunction<FunctionName> getFunctionsID ) throws IOException {
/*
| Offset to the interfaces (4 bytes) |
| Offset to the instanceof (4 bytes) |
| String id of the class name (4 bytes) |
| first vtable entry (4 bytes) |
| ..... |
| interface calls (itable) |
| list of implemented interface |
*/
this.vtableOffset = dataStream.size();
LittleEndianOutputStream header = new LittleEndianOutputStream( dataStream );
@ -452,9 +509,11 @@ public class TypeManager {
data.writeInt32( functIdx );
}
// header position TYPE_DESCRIPTION_INTERFACE_OFFSET
header.writeInt32( data.size() + VTABLE_FIRST_FUNCTION_INDEX * 4 ); // offset of interface calls
//TODO interface calls
// header position TYPE_DESCRIPTION_INSTANCEOF_OFFSET
header.writeInt32( data.size() + VTABLE_FIRST_FUNCTION_INDEX * 4 ); // offset of instanceeof list
data.writeInt32( instanceOFs.size() );
for( StructType type : instanceOFs ) {
@ -462,6 +521,7 @@ public class TypeManager {
}
int classNameIdx = options.strings.get( getName().replace( '/', '.' ) );
// header position TYPE_DESCRIPTION_TYPE_NAME
header.writeInt32( classNameIdx ); // string id of the className
data.writeTo( dataStream );

View File

@ -91,7 +91,7 @@ class WasmStructInstruction extends WasmInstruction {
}
js.append( i ).append( ':' );
NamedStorageType storageType = list.get( i );
if( TypeManager.VTABLE == storageType.getName() ) {
if( TypeManager.FIELD_VTABLE == storageType.getName() ) {
js.append( type.getVTable() );
} else {
AnyType fieldType = storageType.getType();