mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-28 00:25:24 +01:00
call class initializer before access to static fields
This commit is contained in:
parent
710127bb44
commit
69b8db16d1
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package de.inetsoftware.jwebassembly.module;
|
package de.inetsoftware.jwebassembly.module;
|
||||||
|
|
||||||
|
import static de.inetsoftware.jwebassembly.module.WasmCodeBuilder.CLASS_INIT;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@ -260,7 +262,7 @@ class FunctionManager {
|
|||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Iterator<FunctionName> getWriteLaterClinit() {
|
Iterator<FunctionName> getWriteLaterClinit() {
|
||||||
return iterator( entry -> entry.getKey().methodName.equals( "<clinit>" ) && entry.getValue().state != State.None );
|
return iterator( entry -> entry.getKey().methodName.equals( CLASS_INIT ) && entry.getValue().state != State.None );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -299,7 +301,9 @@ class FunctionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* get a iterator for function names
|
* get a iterator for function names
|
||||||
* @param filter the filter
|
*
|
||||||
|
* @param filter
|
||||||
|
* the filter
|
||||||
* @return the iterator
|
* @return the iterator
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@ -85,13 +85,13 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
|
|||||||
reset( code.getLocalVariableTable(), method, null );
|
reset( code.getLocalVariableTable(), method, null );
|
||||||
branchManager.reset( code );
|
branchManager.reset( code );
|
||||||
|
|
||||||
if( "<clinit>".equals( method.getName() ) ) {
|
if( CLASS_INIT.equals( method.getName() ) ) {
|
||||||
// Add a hook to run the class/static constructor only once
|
// Add a hook to run the class/static constructor only once
|
||||||
FunctionName name = new FunctionName( method.getClassName(), "<clisinit>", "" );
|
FunctionName name = new FunctionName( method.getClassName(), "<class_isInit>", "" );
|
||||||
addGlobalInstruction( true, name, ValueType.i32, -1, -1 );
|
addGlobalInstruction( true, name, ValueType.i32, null, -1, -1 );
|
||||||
addBlockInstruction( WasmBlockOperator.BR_IF, 0, -1, -1 );
|
addBlockInstruction( WasmBlockOperator.BR_IF, 0, -1, -1 );
|
||||||
addConstInstruction( 1, ValueType.i32, -1, -1 );
|
addConstInstruction( 1, ValueType.i32, -1, -1 );
|
||||||
addGlobalInstruction( false, name, ValueType.i32, -1, -1 );
|
addGlobalInstruction( false, name, ValueType.i32, null, -1, -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
byteCode = code.getByteCode();
|
byteCode = code.getByteCode();
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package de.inetsoftware.jwebassembly.module;
|
package de.inetsoftware.jwebassembly.module;
|
||||||
|
|
||||||
|
import static de.inetsoftware.jwebassembly.module.WasmCodeBuilder.CLASS_INIT;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -387,7 +389,7 @@ public class ModuleGenerator {
|
|||||||
String className = iterator.next();
|
String className = iterator.next();
|
||||||
ClassFile classFile = classFileLoader.get( className );
|
ClassFile classFile = classFileLoader.get( className );
|
||||||
if( classFile != null ) {
|
if( classFile != null ) {
|
||||||
MethodInfo method = classFile.getMethod( "<clinit>", "()V" );
|
MethodInfo method = classFile.getMethod( CLASS_INIT, "()V" );
|
||||||
if( method != null ) {
|
if( method != null ) {
|
||||||
functions.markAsNeeded( new FunctionName( method ), false );
|
functions.markAsNeeded( new FunctionName( method ), false );
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package de.inetsoftware.jwebassembly.module;
|
package de.inetsoftware.jwebassembly.module;
|
||||||
|
|
||||||
|
import static de.inetsoftware.jwebassembly.module.WasmCodeBuilder.CONSTRUCTOR;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
@ -82,7 +84,8 @@ public class TypeManager {
|
|||||||
public static final int TYPE_DESCRIPTION_INSTANCEOF_OFFSET = 4;
|
public static final int TYPE_DESCRIPTION_INSTANCEOF_OFFSET = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Byte position in the type description that contains the offset to class name idx in the string constant table. Length 4 bytes.
|
* Byte position in the type description that contains the offset to class name idx in the string constant table.
|
||||||
|
* Length 4 bytes.
|
||||||
*/
|
*/
|
||||||
public static final int TYPE_DESCRIPTION_TYPE_NAME = 8;
|
public static final int TYPE_DESCRIPTION_TYPE_NAME = 8;
|
||||||
|
|
||||||
@ -151,7 +154,8 @@ public class TypeManager {
|
|||||||
*
|
*
|
||||||
* @see ReplacementForClass#getPrimitiveClass(String)
|
* @see ReplacementForClass#getPrimitiveClass(String)
|
||||||
*/
|
*/
|
||||||
private static final String[] PRIMITIVE_CLASSES = { "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" };
|
private static final String[] PRIMITIVE_CLASSES =
|
||||||
|
{ "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" };
|
||||||
|
|
||||||
private final Map<Object, StructType> structTypes = new LinkedHashMap<>();
|
private final Map<Object, StructType> structTypes = new LinkedHashMap<>();
|
||||||
|
|
||||||
@ -179,7 +183,9 @@ public class TypeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the type manager
|
* Initialize the type manager
|
||||||
* @param classFileLoader for loading the class files
|
*
|
||||||
|
* @param classFileLoader
|
||||||
|
* for loading the class files
|
||||||
*/
|
*/
|
||||||
void init( ClassFileLoader classFileLoader ) {
|
void init( ClassFileLoader classFileLoader ) {
|
||||||
this.classFileLoader = classFileLoader;
|
this.classFileLoader = classFileLoader;
|
||||||
@ -260,6 +266,7 @@ public class TypeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the function name to get a constant class.
|
* Get the function name to get a constant class.
|
||||||
|
*
|
||||||
* @return the function
|
* @return the function
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -424,8 +431,8 @@ public class TypeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the FunctionName for a virtual call. The function has 2 parameters (THIS,
|
* Create the FunctionName for a virtual call. The function has 2 parameters (THIS, virtualfunctionIndex) and
|
||||||
* virtualfunctionIndex) and returns the index of the function.
|
* returns the index of the function.
|
||||||
*
|
*
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
@ -476,8 +483,7 @@ public class TypeManager {
|
|||||||
+ "local.set 3 " // save $table, the itable start location
|
+ "local.set 3 " // save $table, the itable start location
|
||||||
+ "loop" //
|
+ "loop" //
|
||||||
+ " local.get 3" // get $table
|
+ " local.get 3" // get $table
|
||||||
+ " i32.load offset=0 align=4"
|
+ " i32.load offset=0 align=4" + " local.tee 4" // save $nextClass
|
||||||
+ " local.tee 4" // save $nextClass
|
|
||||||
+ " local.get 1" // get $classIndex
|
+ " local.get 1" // get $classIndex
|
||||||
+ " i32.eq" //
|
+ " i32.eq" //
|
||||||
+ " if" // $nextClass == $classIndex
|
+ " if" // $nextClass == $classIndex
|
||||||
@ -770,7 +776,7 @@ public class TypeManager {
|
|||||||
|
|
||||||
// calculate the vtable (the function indexes of the virtual methods)
|
// calculate the vtable (the function indexes of the virtual methods)
|
||||||
for( MethodInfo method : classFile.getMethods() ) {
|
for( MethodInfo method : classFile.getMethods() ) {
|
||||||
if( method.isStatic() || "<init>".equals( method.getName() ) ) {
|
if( method.isStatic() || CONSTRUCTOR.equals( method.getName() ) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FunctionName funcName = new FunctionName( method );
|
FunctionName funcName = new FunctionName( method );
|
||||||
@ -1017,6 +1023,7 @@ public class TypeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get kind of the StructType
|
* Get kind of the StructType
|
||||||
|
*
|
||||||
* @return the type kind
|
* @return the type kind
|
||||||
*/
|
*/
|
||||||
public StructTypeKind getKind() {
|
public StructTypeKind getKind() {
|
||||||
@ -1025,6 +1032,7 @@ public class TypeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the Java type
|
* Get the name of the Java type
|
||||||
|
*
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -1051,6 +1059,7 @@ public class TypeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the fields of this struct
|
* Get the fields of this struct
|
||||||
|
*
|
||||||
* @return the fields
|
* @return the fields
|
||||||
*/
|
*/
|
||||||
public List<NamedStorageType> getFields() {
|
public List<NamedStorageType> getFields() {
|
||||||
@ -1199,10 +1208,12 @@ public class TypeManager {
|
|||||||
protected boolean hasWasmCode() {
|
protected boolean hasWasmCode() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean istStatic() {
|
protected boolean istStatic() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
|
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
|
||||||
WasmCodeBuilder codebuilder = watParser;
|
WasmCodeBuilder codebuilder = watParser;
|
||||||
@ -1290,9 +1301,12 @@ public class TypeManager {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final List<AnyType> params;
|
private final List<AnyType> params;
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final List<AnyType> results;
|
private final List<AnyType> results;
|
||||||
|
|
||||||
private int code;
|
private int code;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public BlockType( List<AnyType> params, List<AnyType> results ) {
|
public BlockType( List<AnyType> params, List<AnyType> results ) {
|
||||||
|
@ -57,6 +57,12 @@ import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
|
|||||||
*/
|
*/
|
||||||
public abstract class WasmCodeBuilder {
|
public abstract class WasmCodeBuilder {
|
||||||
|
|
||||||
|
/** Java method name of of constructor */
|
||||||
|
static final String CONSTRUCTOR = "<init>";
|
||||||
|
|
||||||
|
/** Java method name of static constructor or initialization method */
|
||||||
|
static final String CLASS_INIT = "<clinit>";
|
||||||
|
|
||||||
private final LocaleVariableManager localVariables;
|
private final LocaleVariableManager localVariables;
|
||||||
|
|
||||||
private final List<WasmInstruction> instructions;
|
private final List<WasmInstruction> instructions;
|
||||||
@ -497,7 +503,16 @@ public abstract class WasmCodeBuilder {
|
|||||||
protected void addGlobalInstruction( boolean load, Member ref, int javaCodePos, int lineNumber ) {
|
protected void addGlobalInstruction( boolean load, Member ref, int javaCodePos, int lineNumber ) {
|
||||||
FunctionName name = new FunctionName( ref );
|
FunctionName name = new FunctionName( ref );
|
||||||
AnyType type = new ValueTypeParser( ref.getType(), types ).next();
|
AnyType type = new ValueTypeParser( ref.getType(), types ).next();
|
||||||
addGlobalInstruction( load, name, type, javaCodePos, lineNumber );
|
FunctionName clinit;
|
||||||
|
if( load ) {
|
||||||
|
clinit = new FunctionName( name.className, CLASS_INIT, "()V" );
|
||||||
|
if( !functions.isUsed( clinit ) ) {
|
||||||
|
clinit = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clinit = null;
|
||||||
|
}
|
||||||
|
addGlobalInstruction( load, name, type, clinit, javaCodePos, lineNumber );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -514,8 +529,8 @@ public abstract class WasmCodeBuilder {
|
|||||||
* @param lineNumber
|
* @param lineNumber
|
||||||
* the line number in the Java source code
|
* the line number in the Java source code
|
||||||
*/
|
*/
|
||||||
protected void addGlobalInstruction( boolean load, FunctionName name, AnyType type, int javaCodePos, int lineNumber ) {
|
protected void addGlobalInstruction( boolean load, FunctionName name, AnyType type, FunctionName clinit, int javaCodePos, int lineNumber ) {
|
||||||
instructions.add( new WasmGlobalInstruction( load, name, type, javaCodePos, lineNumber ) );
|
instructions.add( new WasmGlobalInstruction( load, name, type, clinit, javaCodePos, lineNumber ) );
|
||||||
functions.markClassAsUsed( name.className );
|
functions.markClassAsUsed( name.className );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,7 +670,7 @@ public abstract class WasmCodeBuilder {
|
|||||||
name = functions.markAsNeeded( name, needThisParameter );
|
name = functions.markAsNeeded( name, needThisParameter );
|
||||||
WasmCallInstruction instruction = new WasmCallInstruction( name, javaCodePos, lineNumber, types, needThisParameter );
|
WasmCallInstruction instruction = new WasmCallInstruction( name, javaCodePos, lineNumber, types, needThisParameter );
|
||||||
|
|
||||||
if( "<init>".equals( name.methodName ) ) {
|
if( CONSTRUCTOR.equals( name.methodName ) ) {
|
||||||
// check if there a factory for the constructor in JavaScript then we need to do some more complex patching
|
// check if there a factory for the constructor in JavaScript then we need to do some more complex patching
|
||||||
Function<String, Object> importAnannotation = functions.getImportAnannotation( name );
|
Function<String, Object> importAnannotation = functions.getImportAnannotation( name );
|
||||||
FunctionName factoryName = null;
|
FunctionName factoryName = null;
|
||||||
@ -667,7 +682,7 @@ public abstract class WasmCodeBuilder {
|
|||||||
factoryName = new ImportSyntheticFunctionName( "String", "init", signature, importAnannotation );
|
factoryName = new ImportSyntheticFunctionName( "String", "init", signature, importAnannotation );
|
||||||
} else {
|
} else {
|
||||||
MethodInfo replace = functions.replace( name, null );
|
MethodInfo replace = functions.replace( name, null );
|
||||||
if( replace != null && !"<init>".equals( replace.getName() ) ) {
|
if( replace != null && !CONSTRUCTOR.equals( replace.getName() ) ) {
|
||||||
// the constructor was replaced with a factory method. Typical this method called then a JavaScript replacement
|
// the constructor was replaced with a factory method. Typical this method called then a JavaScript replacement
|
||||||
factoryName = new FunctionName( replace );
|
factoryName = new FunctionName( replace );
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 - 2021 Volker Berlin (i-net software)
|
Copyright 2018 - 2022 Volker Berlin (i-net software)
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -19,6 +19,7 @@ package de.inetsoftware.jwebassembly.module;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import de.inetsoftware.jwebassembly.wasm.AnyType;
|
import de.inetsoftware.jwebassembly.wasm.AnyType;
|
||||||
|
|
||||||
@ -36,6 +37,8 @@ class WasmGlobalInstruction extends WasmInstruction {
|
|||||||
|
|
||||||
private AnyType type;
|
private AnyType type;
|
||||||
|
|
||||||
|
private FunctionName clinit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of a load/store instruction
|
* Create an instance of a load/store instruction
|
||||||
*
|
*
|
||||||
@ -45,16 +48,19 @@ class WasmGlobalInstruction extends WasmInstruction {
|
|||||||
* the name of the static field
|
* the name of the static field
|
||||||
* @param type
|
* @param type
|
||||||
* the type of the static field
|
* the type of the static field
|
||||||
|
* @param clinit
|
||||||
|
* a reference to the class/static constructor which should executed before access a static field
|
||||||
* @param javaCodePos
|
* @param javaCodePos
|
||||||
* the code position/offset in the Java method
|
* the code position/offset in the Java method
|
||||||
* @param lineNumber
|
* @param lineNumber
|
||||||
* the line number in the Java source code
|
* the line number in the Java source code
|
||||||
*/
|
*/
|
||||||
WasmGlobalInstruction( boolean load, @Nonnull FunctionName name, AnyType type, int javaCodePos, int lineNumber ) {
|
WasmGlobalInstruction( boolean load, @Nonnull FunctionName name, AnyType type, @Nullable FunctionName clinit, int javaCodePos, int lineNumber ) {
|
||||||
super( javaCodePos, lineNumber );
|
super( javaCodePos, lineNumber );
|
||||||
this.load = load;
|
this.load = load;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.clinit = clinit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,6 +86,9 @@ class WasmGlobalInstruction extends WasmInstruction {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
|
public void writeTo( @Nonnull ModuleWriter writer ) throws IOException {
|
||||||
|
if( clinit != null ) {
|
||||||
|
writer.writeFunctionCall( clinit, clinit.signatureName );
|
||||||
|
}
|
||||||
writer.writeGlobalAccess( load, name, type );
|
writer.writeGlobalAccess( load, name, type );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user