From d103f97c04868308052a106ecfe89a4b80c04240 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Fri, 3 Jan 2020 19:51:58 +0100 Subject: [PATCH] replace also a class with annotation --- .../inetsoftware/classparser/ClassFile.java | 51 ++++++++++++++++--- .../classparser/ConstantPool.java | 23 ++++++++- .../jwebassembly/module/ClassFileLoader.java | 25 +++++++-- .../jwebassembly/module/ModuleGenerator.java | 15 +++++- 4 files changed, 101 insertions(+), 13 deletions(-) diff --git a/src/de/inetsoftware/classparser/ClassFile.java b/src/de/inetsoftware/classparser/ClassFile.java index fa0332a..2944d04 100644 --- a/src/de/inetsoftware/classparser/ClassFile.java +++ b/src/de/inetsoftware/classparser/ClassFile.java @@ -34,8 +34,6 @@ import de.inetsoftware.classparser.Attributes.AttributeInfo; */ public class ClassFile { - private final DataInputStream input; - private final int minorVersion; private final int majorVersion; @@ -71,7 +69,7 @@ public class ClassFile { * if this input stream reaches the end before reading the class file. */ public ClassFile( InputStream stream ) throws IOException { - this.input = new DataInputStream( stream ); + DataInputStream input = new DataInputStream( stream ); int magic = input.readInt(); if( magic != 0xCAFEBABE ) { throw new IOException( "Invalid class magic: " + Integer.toHexString( magic ) ); @@ -87,8 +85,8 @@ public class ClassFile { for( int i = 0; i < interfaces.length; i++ ) { interfaces[i] = (ConstantClass)constantPool.get( input.readUnsignedShort() ); } - fields = readFields(); - methods = readMethods(); + fields = readFields( input ); + methods = readMethods( input ); attributes = new Attributes( input, constantPool ); stream.close(); @@ -117,6 +115,45 @@ public class ClassFile { } } + /** + * Create a replaced instance. + * + * @param className + * the class name that should be replaced + * @param classFile + * the replacing class file data + */ + public ClassFile( String className, ClassFile classFile ) { + minorVersion = classFile.minorVersion; + majorVersion = classFile.majorVersion; + + constantPool = classFile.constantPool; + accessFlags = classFile.accessFlags; + thisClass = new ConstantClass( className ); + superClass = classFile.superClass; + interfaces = classFile.interfaces; + fields = classFile.fields; + methods = classFile.methods; + attributes = classFile.attributes; + + // patch constant pool + String origClassName = classFile.thisClass.getName(); + for( int i = 0; i < constantPool.size(); i++ ) { + Object obj = constantPool.get( i ); + if( obj instanceof ConstantClass ) { + if( ((ConstantClass)obj).getName().equals( origClassName ) ) { + constantPool.set( i, thisClass ); + } + } else if( obj instanceof ConstantFieldRef ) { + ConstantFieldRef ref = (ConstantFieldRef)obj; + if( ref.getClassName().equals( origClassName ) ) { + ConstantNameAndType nameAndType = new ConstantNameAndType( ref.getName(), ref.getType() ); + constantPool.set( i, new ConstantFieldRef( thisClass, nameAndType ) ); + } + } + } + } + /** * Get value of SourceFile if available. * @@ -216,7 +253,7 @@ public class ClassFile { return accessFlags; } - private FieldInfo[] readFields() throws IOException { + private FieldInfo[] readFields( DataInputStream input ) throws IOException { FieldInfo[] fields = new FieldInfo[input.readUnsignedShort()]; for( int i = 0; i < fields.length; i++ ) { fields[i] = new FieldInfo( input, constantPool ); @@ -224,7 +261,7 @@ public class ClassFile { return fields; } - private MethodInfo[] readMethods() throws IOException { + private MethodInfo[] readMethods( DataInputStream input ) throws IOException { MethodInfo[] methods = new MethodInfo[input.readUnsignedShort()]; for( int i = 0; i < methods.length; i++ ) { methods[i] = new MethodInfo( input, constantPool, this ); diff --git a/src/de/inetsoftware/classparser/ConstantPool.java b/src/de/inetsoftware/classparser/ConstantPool.java index 6efc631..978eb20 100644 --- a/src/de/inetsoftware/classparser/ConstantPool.java +++ b/src/de/inetsoftware/classparser/ConstantPool.java @@ -1,5 +1,5 @@ /* - Copyright 2011 - 2019 Volker Berlin (i-net software) + Copyright 2011 - 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. @@ -147,4 +147,25 @@ public class ConstantPool { public Object get( int index ) { return constantPool[index]; } + + /** + * Set a value in the constant pool. + * + * @param index + * the index + * @param value + * the new value + */ + void set( int index, Object value ) { + constantPool[index] = value; + } + + /** + * Get the count of entries in the pool. + * + * @return the count + */ + int size() { + return constantPool.length; + } } diff --git a/src/de/inetsoftware/jwebassembly/module/ClassFileLoader.java b/src/de/inetsoftware/jwebassembly/module/ClassFileLoader.java index cbe9b42..6773714 100644 --- a/src/de/inetsoftware/jwebassembly/module/ClassFileLoader.java +++ b/src/de/inetsoftware/jwebassembly/module/ClassFileLoader.java @@ -18,6 +18,7 @@ package de.inetsoftware.jwebassembly.module; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; import javax.annotation.Nullable; @@ -31,7 +32,9 @@ import de.inetsoftware.classparser.WeakValueCache; */ public class ClassFileLoader { - private final WeakValueCache CACHE = new WeakValueCache<>(); + private final HashMap replace = new HashMap<>(); + + private final WeakValueCache weakCache = new WeakValueCache<>(); private final ClassLoader loader; @@ -56,16 +59,32 @@ public class ClassFileLoader { */ @Nullable public ClassFile get( String className ) throws IOException { - ClassFile classFile = CACHE.get( className ); + ClassFile classFile = replace.get( className ); + if( classFile != null ) { + return classFile; + } + classFile = weakCache.get( className ); if( classFile != null ) { return classFile; } InputStream stream = loader.getResourceAsStream( className + ".class" ); if( stream != null ) { classFile = new ClassFile( stream ); - CACHE.put( className, classFile ); + weakCache.put( className, classFile ); } return classFile; } + /** + * Replace the class in the cache with the given instance. + * + * @param className + * the name of the class to replace + * @param classFile + * the replasing ClassFile + */ + public void replace( String className, ClassFile classFile ) { + classFile = new ClassFile( className, classFile ); + replace.put( className, classFile ); + } } diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java index bd4e1f2..6d4b9a4 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2019 Volker Berlin (i-net software) + * Copyright 2017 - 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. @@ -158,8 +158,19 @@ public class ModuleGenerator { * the class file * @throws WasmException * if some Java code can't converted + * @throws IOException + * if any I/O error occur */ - public void prepare( ClassFile classFile ) { + public void prepare( ClassFile classFile ) throws IOException { + // check if this class replace another class + Map annotationValues; + if( (annotationValues = classFile.getAnnotation( JWebAssembly.REPLACE_ANNOTATION )) != null ) { + String signatureName = (String)annotationValues.get( "value" ); + if( signatureName != null ) { + classFileLoader.replace( signatureName, classFile ); + } + } + iterateMethods( classFile, m -> prepareMethod( m ) ); }