From 9dfbba36fb22504f4711b455e35eece6370fbf27 Mon Sep 17 00:00:00 2001
From: Volker Berlin <volker.berlin@googlemail.com>
Date: Tue, 21 Mar 2017 22:38:52 +0100
Subject: [PATCH] Split the TextModulWriter in an abstract writer which can
 also use for the binary format and the simple formatter. Some first byte code
 operations.

---
 .../jwebassembly/modul/ModulWriter.java       | 260 ++++++++++++++++++
 .../jwebassembly/modul/ValueType.java         |  26 ++
 .../jwebassembly/text/TextModulWriter.java    | 152 +++++-----
 .../samples/FunctionParameters.wat            |   1 +
 4 files changed, 350 insertions(+), 89 deletions(-)
 create mode 100644 src/de/inetsoftware/jwebassembly/modul/ModulWriter.java
 create mode 100644 src/de/inetsoftware/jwebassembly/modul/ValueType.java

diff --git a/src/de/inetsoftware/jwebassembly/modul/ModulWriter.java b/src/de/inetsoftware/jwebassembly/modul/ModulWriter.java
new file mode 100644
index 0000000..f096ae3
--- /dev/null
+++ b/src/de/inetsoftware/jwebassembly/modul/ModulWriter.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017 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.modul;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+import de.inetsoftware.classparser.ClassFile;
+import de.inetsoftware.classparser.Code;
+import de.inetsoftware.classparser.CodeInputStream;
+import de.inetsoftware.classparser.LineNumberTable;
+import de.inetsoftware.classparser.MethodInfo;
+import de.inetsoftware.jwebassembly.WasmException;
+
+/**
+ * Module Writer for text format with S-expressions.
+ * 
+ * @author Volker Berlin
+ */
+public abstract class ModulWriter implements Closeable {
+
+    /**
+     * Write the content of the class to the
+     * 
+     * @param classFile
+     *            the class file
+     * @throws IOException
+     *             if any I/O error occur
+     * @throws WasmException
+     *             if some Java code can't converted
+     */
+    public void write( ClassFile classFile ) throws IOException, WasmException {
+        MethodInfo[] methods = classFile.getMethods();
+        for( MethodInfo method : methods ) {
+            Code code = method.getCode();
+            if( method.getName().equals( "<init>" ) && method.getDescription().equals( "()V" )
+                            && code.isSuperInitReturn( classFile.getSuperClass() ) ) {
+                continue; //default constructor
+            }
+            writeMethod( method );
+        }
+    }
+
+    /**
+     * Write the content of a method.
+     * 
+     * @param method
+     *            the method
+     * @throws IOException
+     *             if any I/O error occur
+     * @throws WasmException
+     *             if some Java code can't converted
+     */
+    private void writeMethod( MethodInfo method ) throws IOException, WasmException {
+        writeMethodStart( method.getName() );
+        writeMethodSignature( method );
+        Code code = method.getCode();
+        if( code != null ) {
+            LineNumberTable lineNumberTable = code.getLineNumberTable();
+            if( lineNumberTable != null ) {
+                int lineNumber;
+                for( int i = 0; i < lineNumberTable.size(); i++ ) {
+                    lineNumber = lineNumberTable.getLineNumber( i );
+                    int offset = lineNumberTable.getStartOffset( i );
+                    int nextOffset =
+                                    i + 1 == lineNumberTable.size() ? code.getCodeSize()
+                                                    : lineNumberTable.getStartOffset( i + 1 );
+                    CodeInputStream byteCode = code.getByteCode( offset, nextOffset - offset );
+                    writeCodeChunk( byteCode, lineNumber );
+                }
+            } else {
+                CodeInputStream byteCode = code.getByteCode();
+                writeCodeChunk( byteCode, -1 );
+            }
+        }
+        writeMethodFinish();
+    }
+
+    /**
+     * Write the method header.
+     * 
+     * @param name
+     *            the method name
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeMethodStart( String name ) throws IOException;
+
+    /**
+     * Write the parameter and return signatures
+     * 
+     * @param method
+     *            the method
+     * @throws IOException
+     *             if any I/O error occur
+     * @throws WasmException
+     *             if some Java code can't converted
+     */
+    private void writeMethodSignature( MethodInfo method ) throws IOException, WasmException {
+        String signature = method.getDescription();
+        String kind = "param";
+        for( int i = 1; i < signature.length(); i++ ) {
+            String javaType;
+            switch( signature.charAt( i ) ) {
+                case '[': // array
+                    javaType = "array";
+                    break;
+                case 'L':
+                    javaType = "object";
+                    break;
+                case 'B': // byte
+                    javaType = "byte";
+                    break;
+                case 'C': // char
+                    javaType = "char";
+                    break;
+                case 'D': // double
+                    writeMethodParam( kind, ValueType.f64 );
+                    continue;
+                case 'F': // float
+                    writeMethodParam( kind, ValueType.f32 );
+                    continue;
+                case 'I': // int
+                    writeMethodParam( kind, ValueType.i32 );
+                    continue;
+                case 'J': // long
+                    writeMethodParam( kind, ValueType.i64 );
+                    continue;
+                case 'V': // void
+                    continue;
+                case ')':
+                    kind = "return";
+                    continue;
+                default:
+                    javaType = signature.substring( i, i + 1 );
+            }
+            Code code = method.getCode();
+            int lineNumber = code == null ? -1 : code.getFirstLineNr();
+            throw new WasmException( "Not supported Java data type in method signature: " + javaType, lineNumber );
+        }
+    }
+
+    /**
+     * Write a method parameter.
+     * 
+     * @param kind
+     *            "param", "return" or "local"
+     * @param valueType
+     *            the data type of the parameter
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeMethodParam( String kind, ValueType valueType ) throws IOException;
+
+    /**
+     * Complete the method
+     * 
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeMethodFinish() throws IOException;
+
+    /**
+     * Write a chunk of byte code.
+     * 
+     * @param byteCode
+     *            a stream of byte code
+     * @param lineNumber
+     *            the current line number
+     * @throws WasmException
+     *             if some Java code can't converted
+     */
+    private void writeCodeChunk( CodeInputStream byteCode, int lineNumber ) throws WasmException {
+        try {
+            while( byteCode.available() > 0 ) {
+                int op = byteCode.readUnsignedByte();
+                switch( op ) {
+                    case 4: // iconst_1
+                        writeConstInt( 1 );
+                        break;
+                    case 26: // iload_0
+                        writeLoadInt( 0 );
+                        break;
+                    case 60: // istore_1
+                        writeStoreInt( 1 );
+                        break;
+                    case 96: // iadd
+                        writeAddInt();
+                        break;
+                    case 177: // return void
+                        writeReturn();
+                        break;
+                    default:
+                        throw new WasmException( "Unimplemented byte code operation: " + op, lineNumber );
+                }
+            }
+        } catch( Exception ex ) {
+            throw WasmException.create( ex, lineNumber );
+        }
+    }
+
+    /**
+     * Write a const integer value
+     * 
+     * @param value
+     *            the value
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeConstInt( int value ) throws IOException;
+
+    /**
+     * Write a load integer
+     * 
+     * @param idx
+     *            the index of the parameter variable
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeLoadInt( int idx ) throws IOException;
+
+    /**
+     * Write a store integer.
+     * 
+     * @param idx
+     *            the index of the parameter variable
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeStoreInt( int idx ) throws IOException;
+
+    /**
+     * Write a add operator
+     * 
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeAddInt() throws IOException;
+
+    /**
+     * Write a return
+     * 
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    protected abstract void writeReturn() throws IOException;
+}
diff --git a/src/de/inetsoftware/jwebassembly/modul/ValueType.java b/src/de/inetsoftware/jwebassembly/modul/ValueType.java
new file mode 100644
index 0000000..5c7666f
--- /dev/null
+++ b/src/de/inetsoftware/jwebassembly/modul/ValueType.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017 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.modul;
+
+/**
+ * @author Volker Berlin
+ */
+public enum ValueType {
+    i32,
+    i64,
+    f32,
+    f64
+}
diff --git a/src/de/inetsoftware/jwebassembly/text/TextModulWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModulWriter.java
index af88708..131fdda 100644
--- a/src/de/inetsoftware/jwebassembly/text/TextModulWriter.java
+++ b/src/de/inetsoftware/jwebassembly/text/TextModulWriter.java
@@ -15,13 +15,10 @@
  */
 package de.inetsoftware.jwebassembly.text;
 
-import java.io.Closeable;
 import java.io.IOException;
 
-import de.inetsoftware.classparser.ClassFile;
-import de.inetsoftware.classparser.Code;
-import de.inetsoftware.classparser.MethodInfo;
-import de.inetsoftware.jwebassembly.WasmException;
+import de.inetsoftware.jwebassembly.modul.ModulWriter;
+import de.inetsoftware.jwebassembly.modul.ValueType;
 
 /**
  * Module Writer for text format with S-expressions.
@@ -29,7 +26,7 @@ import de.inetsoftware.jwebassembly.WasmException;
  * @author Volker Berlin
  *
  */
-public class TextModulWriter implements Closeable {
+public class TextModulWriter extends ModulWriter {
 
     private Appendable output;
 
@@ -50,7 +47,7 @@ public class TextModulWriter implements Closeable {
     }
 
     /**
-     * 
+     * {@inheritDoc}
      */
     @Override
     public void close() throws IOException {
@@ -60,100 +57,77 @@ public class TextModulWriter implements Closeable {
     }
 
     /**
-     * Write the content of the class to the
-     * 
-     * @param classFile
-     *            the class file
-     * @throws IOException
-     *             if any I/O error occur
-     * @throws WasmException
-     *             if some Java code can't converted
+     * {@inheritDoc}
      */
-    public void write( ClassFile classFile ) throws IOException, WasmException {
-        MethodInfo[] methods = classFile.getMethods();
-        for( MethodInfo method : methods ) {
-            Code code = method.getCode();
-            if( method.getName().equals( "<init>" ) && method.getDescription().equals( "()V" )
-                            && code.isSuperInitReturn( classFile.getSuperClass() ) ) {
-                continue; //default constructor
-            }
-            writeMethod( method );
-        }
+    @Override
+    protected void writeMethodStart( String name ) throws IOException {
+        newline();
+        output.append( "(func $" );
+        output.append( name );
+        inset++;
     }
 
     /**
-     * Write the content of a method.
-     * 
-     * @param method
-     *            the method
-     * @throws IOException
-     *             if any I/O error occur
-     * @throws WasmException
-     *             if some Java code can't converted
+     * {@inheritDoc}
      */
-    private void writeMethod( MethodInfo method ) throws IOException, WasmException {
-        newline();
-        output.append( "(func $" );
-        output.append( method.getName() );
-        writeMethodSignature( method );
-        inset++;
+    @Override
+    protected void writeMethodParam( String kind, ValueType valueType ) throws IOException {
+        output.append( " (" ).append( kind ).append( ' ' ).append( valueType.toString() ).append( ')' );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void writeMethodFinish() throws IOException {
         inset--;
         newline();
         output.append( ')' );
     }
 
     /**
-     * Write the parameter and return signatures
-     * 
-     * @param method
-     *            the method
-     * @throws IOException
-     *             if any I/O error occur
-     * @throws WasmException
-     *             if some Java code can't converted
+     * {@inheritDoc}
      */
-    private void writeMethodSignature( MethodInfo method ) throws IOException, WasmException {
-        String signature = method.getDescription();
-        String kind = "param";
-        for( int i = 1; i < signature.length(); i++ ) {
-            String javaType;
-            switch( signature.charAt( i ) ) {
-                case '[': // array
-                    javaType = "array";
-                    break;
-                case 'L':
-                    javaType = "object";
-                    break;
-                case 'B': // byte
-                    javaType = "byte";
-                    break;
-                case 'C': // char
-                    javaType = "char";
-                    break;
-                case 'D': // double
-                    output.append( " (" ).append( kind ).append( " f64)" );
-                    continue;
-                case 'F': // float
-                    output.append( " (" ).append( kind ).append( " f32)" );
-                    continue;
-                case 'I': // int
-                    output.append( " (" ).append( kind ).append( " i32)" );
-                    continue;
-                case 'J': // long
-                    output.append( " (" ).append( kind ).append( " i64)" );
-                    continue;
-                case 'V': // void
-                    continue;
-                case ')':
-                    kind = "return";
-                    continue;
-                default:
-                    javaType = signature.substring( i, i + 1 );
-            }
-            Code code = method.getCode();
-            int lineNumber = code == null ? -1 : code.getFirstLineNr();
-            throw new WasmException( "Not supported Java data type in method signature: " + javaType, lineNumber );
-        }
+    @Override
+    protected void writeConstInt( int value ) throws IOException {
+        newline();
+        output.append( "i32.const " ).append( Integer.toString( value ));
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void writeLoadInt( int idx ) throws IOException {
+        newline();
+        output.append( "get_local " ).append( Integer.toString( idx ) );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void writeStoreInt( int idx ) throws IOException {
+        newline();
+        output.append( "set_local " ).append( Integer.toString( idx ) );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void writeAddInt() throws IOException {
+        newline();
+        output.append( "i32.add" );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void writeReturn() throws IOException {
+        newline();
+        output.append( "return" );
     }
 
     /**
diff --git a/test/de/inetsoftware/jwebassembly/samples/FunctionParameters.wat b/test/de/inetsoftware/jwebassembly/samples/FunctionParameters.wat
index 3887de9..ec0f0ff 100644
--- a/test/de/inetsoftware/jwebassembly/samples/FunctionParameters.wat
+++ b/test/de/inetsoftware/jwebassembly/samples/FunctionParameters.wat
@@ -1,4 +1,5 @@
 (module
   (func $singleInt (param i32)
+    return
   )
 )
\ No newline at end of file