/* * 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. * 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; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.net.MalformedURLException; import java.net.URL; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.logging.Formatter; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.StreamHandler; import javax.annotation.Nonnull; import de.inetsoftware.classparser.ClassFile; import de.inetsoftware.jwebassembly.binary.BinaryModuleWriter; import de.inetsoftware.jwebassembly.module.ModuleGenerator; import de.inetsoftware.jwebassembly.module.ModuleWriter; import de.inetsoftware.jwebassembly.module.WasmOptions; import de.inetsoftware.jwebassembly.module.WasmTarget; import de.inetsoftware.jwebassembly.text.TextModuleWriter; /** * The main class of the compiler. * * @author Volker Berlin */ public class JWebAssembly { private final List<URL> classFiles = new ArrayList<>(); private final HashMap<String, String> properties = new HashMap<>(); private final List<URL> libraries = new ArrayList<>(); /** * Property for adding debug names to the output if true. */ public static final String DEBUG_NAMES = "DebugNames"; /** * Property for relative path between the final wasm file location and the source files location for the source map. * If not empty it should end with a slash like "../../src/main/java/". */ public static final String SOURCE_MAP_BASE = "SourceMapBase"; /** * The name of the annotation for import functions. */ public static final String IMPORT_ANNOTATION = "de.inetsoftware.jwebassembly.api.annotation.Import"; /** * The name of the annotation for export functions. */ public static final String EXPORT_ANNOTATION = "de.inetsoftware.jwebassembly.api.annotation.Export"; /** * The name of the annotation for native WASM code in text format. */ public static final String TEXTCODE_ANNOTATION = "de.inetsoftware.jwebassembly.api.annotation.WasmTextCode"; /** * The name of the annotation for replacing a single method of the Java runtime. */ public static final String REPLACE_ANNOTATION = "de.inetsoftware.jwebassembly.api.annotation.Replace"; /** * The name of the annotation for partial class another class of the Java runtime. */ public static final String PARTIAL_ANNOTATION = "de.inetsoftware.jwebassembly.api.annotation.Partial"; /** * If the GC feature of WASM should be use or the GC of the JavaScript host. If true use the GC instructions of WASM. */ public static final String WASM_USE_GC = "wasm.use_gc"; /** * If the exception handling feature of WASM should be use or an unreachable instruction. If true use the exception instructions of WASM. */ public static final String WASM_USE_EH = "wasm.use_eh"; /** * The logger instance */ public static final Logger LOGGER = Logger.getAnonymousLogger( null ); static { LOGGER.setUseParentHandlers( false ); Formatter formatter = new Formatter() { @Override public String format( LogRecord record ) { String msg = record.getMessage() + '\n'; if( record.getThrown() != null ) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter( sw ); record.getThrown().printStackTrace( pw ); pw.close(); msg += sw.toString(); } return msg; } }; StreamHandler handler = new StreamHandler( System.out, formatter ) { @Override public void publish(LogRecord record) { super.publish(record); flush(); } }; handler.setLevel( Level.ALL ); LOGGER.addHandler( handler ); //LOGGER.setLevel( Level.FINE ); } /** * Create a instance. */ public JWebAssembly() { ProtectionDomain protectionDomain = getClass().getProtectionDomain(); if( protectionDomain != null ) { libraries.add( protectionDomain.getCodeSource().getLocation() ); // add the compiler self to the library path } } /** * Add a classFile to compile * * @param classFile * the file */ public void addFile( @Nonnull File classFile ) { try { classFiles.add( classFile.toURI().toURL() ); } catch( MalformedURLException ex ) { throw new IllegalArgumentException( ex ); } } /** * Add a classFile to compile * * @param classFile * the file */ public void addFile( @Nonnull URL classFile ) { classFiles.add( classFile ); } /** * Set property to control the behavior of the compiler * * @param key * the key * @param value * the new value */ public void setProperty( String key, String value ) { properties.put( key, value ); } /** * Get the value of a property. * * @param key * the key * @return the current value */ public String getProperty( String key ) { return properties.get( key ); } /** * Add a jar or zip file as library to the compiler. Methods from the library will be add to the wasm only when used. * * @param library * a archive file */ public void addLibrary( @Nonnull File library ) { try { addLibrary( library.toURI().toURL() ); } catch( MalformedURLException ex ) { throw new IllegalArgumentException( ex ); } } /** * Add a jar or zip file as library to the compiler. Methods from the library will be add to the wasm only when used. * * @param library * a archive file */ public void addLibrary( @Nonnull URL library ) { libraries.add( library ); } /** * Convert the added files to a WebAssembly module in text representation. * * @return the module as string * @throws WasmException * if any conversion error occurs */ public String compileToText() throws WasmException { StringBuilder output = new StringBuilder(); try { compileToText( output ); return output.toString(); } catch( Exception ex ) { System.err.println( output ); throw ex; } } /** * Convert the added files to a WebAssembly module in text representation. * * @param file * the target for the module data * @throws WasmException * if any conversion error occurs */ public void compileToText( File file ) throws WasmException { try (WasmTarget target = new WasmTarget( file )) { compileToText( target ); } catch( Exception ex ) { throw WasmException.create( ex ); } } /** * Convert the added files to a WebAssembly module in text representation. * * @param output * the target for the module data * @throws WasmException * if any conversion error occurs */ public void compileToText( Appendable output ) throws WasmException { try (WasmTarget target = new WasmTarget( output )) { compileToText( target ); } catch( Exception ex ) { throw WasmException.create( ex ); } } /** * Convert the added files to a WebAssembly module in text representation. * * @param target * the target for the module data * @throws WasmException * if any conversion error occurs */ private void compileToText( WasmTarget target ) throws WasmException { try (TextModuleWriter writer = new TextModuleWriter( target, new WasmOptions( properties ) )) { compile( writer, target ); } catch( Exception ex ) { throw WasmException.create( ex ); } } /** * Convert the added files to a WebAssembly module in binary representation. * * @return the module as string * @throws WasmException * if any conversion error occurs */ public byte[] compileToBinary() throws WasmException { ByteArrayOutputStream output = new ByteArrayOutputStream(); compileToBinary( output ); return output.toByteArray(); } /** * Convert the added files to a WebAssembly module in binary representation. * * @param file * the target for the module data * @throws WasmException * if any conversion error occurs */ public void compileToBinary( File file ) throws WasmException { try (WasmTarget target = new WasmTarget( file ) ) { compileToBinary( target ); } catch( Exception ex ) { throw WasmException.create( ex ); } } /** * Convert the added files to a WebAssembly module in binary representation. * * @param output * the target for the module data * @throws WasmException * if any conversion error occurs */ public void compileToBinary( OutputStream output ) throws WasmException { try (WasmTarget target = new WasmTarget( output )) { compileToBinary( target ); } catch( Exception ex ) { throw WasmException.create( ex ); } } /** * Convert the added files to a WebAssembly module in binary representation. * * @param target * the target for the module data * @throws WasmException * if any conversion error occurs */ private void compileToBinary( WasmTarget target ) throws WasmException { try (BinaryModuleWriter writer = new BinaryModuleWriter( target, new WasmOptions( properties ) )) { compile( writer, target ); } catch( Exception ex ) { throw WasmException.create( ex ); } } /** * Convert the added files to a WebAssembly module. * * @param writer * the formatter * @param target * the target for the module data * @throws IOException * if any I/O error occur * @throws WasmException * if any conversion error occurs */ private void compile( ModuleWriter writer, WasmTarget target ) throws IOException, WasmException { ModuleGenerator generator = new ModuleGenerator( writer, target, libraries ); for( URL url : classFiles ) { try { ClassFile classFile = new ClassFile( new BufferedInputStream( url.openStream() ) ); generator.prepare( classFile ); } catch( IOException ex ) { throw WasmException.create( "Parsing of file " + url + " failed.", ex ); } } generator.prepareFinish(); generator.finish(); } }