Can compile recursive factorial
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 27 Aug 2012 13:16:29 +0200
changeset 4f352a33fb71b
parent 3 e44f0155d946
child 5 d3193a7086e7
Can compile recursive factorial
src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java
src/test/java/org/apidesign/java4browser/StaticMethod.java
src/test/java/org/apidesign/java4browser/StaticMethodTest.java
     1.1 --- a/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java	Mon Aug 27 06:12:35 2012 +0200
     1.2 +++ b/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java	Mon Aug 27 13:16:29 2012 +0200
     1.3 @@ -21,6 +21,7 @@
     1.4  import java.io.InputStream;
     1.5  import java.util.List;
     1.6  import static org.netbeans.modules.classfile.ByteCodes.*;
     1.7 +import org.netbeans.modules.classfile.CPMethodInfo;
     1.8  import org.netbeans.modules.classfile.ClassFile;
     1.9  import org.netbeans.modules.classfile.Code;
    1.10  import org.netbeans.modules.classfile.Method;
    1.11 @@ -95,9 +96,10 @@
    1.12      }
    1.13  
    1.14      private void produceCode(byte[] byteCodes) throws IOException {
    1.15 +        out.append("\nvar gt = 0;\nfor(;;) switch(gt) {\n");
    1.16          for (int i = 0; i < byteCodes.length; i++) {
    1.17              int prev = i;
    1.18 -            out.append("  ");
    1.19 +            out.append("  case " + i).append(": ");
    1.20              final int c = (byteCodes[i] + 256) % 256;
    1.21              switch (c) {
    1.22                  case bc_aload_0:
    1.23 @@ -191,6 +193,79 @@
    1.24                  case bc_i2s:
    1.25                      out.append("/* number conversion */");
    1.26                      break;
    1.27 +                case bc_iconst_0:
    1.28 +                case bc_dconst_0:
    1.29 +                case bc_lconst_0:
    1.30 +                case bc_fconst_0:
    1.31 +                    out.append("stack.push(0);");
    1.32 +                    break;
    1.33 +                case bc_iconst_1:
    1.34 +                case bc_lconst_1:
    1.35 +                case bc_fconst_1:
    1.36 +                case bc_dconst_1:
    1.37 +                    out.append("stack.push(1);");
    1.38 +                    break;
    1.39 +                case bc_iconst_2:
    1.40 +                case bc_fconst_2:
    1.41 +                    out.append("stack.push(2);");
    1.42 +                    break;
    1.43 +                case bc_iconst_3:
    1.44 +                    out.append("stack.push(3);");
    1.45 +                    break;
    1.46 +                case bc_iconst_4:
    1.47 +                    out.append("stack.push(4);");
    1.48 +                    break;
    1.49 +                case bc_iconst_5:
    1.50 +                    out.append("stack.push(5);");
    1.51 +                    break;
    1.52 +                case bc_if_icmpeq: {
    1.53 +                    i = generateIf(byteCodes, i, "==");
    1.54 +                    break;
    1.55 +                }
    1.56 +                case bc_if_icmpne:
    1.57 +                    i = generateIf(byteCodes, i, "!=");
    1.58 +                    break;
    1.59 +                case bc_if_icmplt:
    1.60 +                    i = generateIf(byteCodes, i, ">");
    1.61 +                    break;
    1.62 +                case bc_if_icmple:
    1.63 +                    i = generateIf(byteCodes, i, ">=");
    1.64 +                    break;
    1.65 +                case bc_if_icmpgt:
    1.66 +                    i = generateIf(byteCodes, i, "<");
    1.67 +                    break;
    1.68 +                case bc_if_icmpge:
    1.69 +                    i = generateIf(byteCodes, i, "<=");
    1.70 +                    break;
    1.71 +                case bc_invokestatic: {
    1.72 +                    int methodIndex = readIntArg(byteCodes, i);
    1.73 +                    CPMethodInfo mi = (CPMethodInfo) jc.getConstantPool().get(methodIndex);
    1.74 +                    boolean[] hasReturn = { false };
    1.75 +                    StringBuilder signature = new StringBuilder();
    1.76 +                    int cnt = countArgs(mi.getDescriptor(), hasReturn, signature);
    1.77 +                    
    1.78 +                    if (hasReturn[0]) {
    1.79 +                        out.append("stack.push(");
    1.80 +                    }
    1.81 +                    out.append(mi.getClassName().getInternalName().replace('/', '_'));
    1.82 +                    out.append('_');
    1.83 +                    out.append(mi.getName());
    1.84 +                    out.append(signature.toString());
    1.85 +                    out.append('(');
    1.86 +                    String sep = "";
    1.87 +                    for (int j = 0; j < cnt; j++) {
    1.88 +                        out.append(sep);
    1.89 +                        out.append("stack.pop()");
    1.90 +                        sep = ", ";
    1.91 +                    }
    1.92 +                    out.append(")");
    1.93 +                    if (hasReturn[0]) {
    1.94 +                        out.append(")");
    1.95 +                    }
    1.96 +                    out.append(";");
    1.97 +                    i += 2;
    1.98 +                    break;
    1.99 +                }
   1.100              }
   1.101              out.append(" /*");
   1.102              for (int j = prev; j <= i; j++) {
   1.103 @@ -200,5 +275,71 @@
   1.104              }
   1.105              out.append("*/\n");
   1.106          }
   1.107 +        out.append("}\n");
   1.108 +    }
   1.109 +
   1.110 +    private int generateIf(byte[] byteCodes, int i, final String test) throws IOException {
   1.111 +        int indx = i + readIntArg(byteCodes, i);
   1.112 +        out.append("if (stack.pop() ").append(test).append(" stack.pop()) { gt = " + indx);
   1.113 +        out.append("; continue; }");
   1.114 +        return i + 2;
   1.115 +    }
   1.116 +
   1.117 +    private int readIntArg(byte[] byteCodes, int offsetInstruction) {
   1.118 +        final int indxHi = (byteCodes[offsetInstruction + 1] + 256) % 256;
   1.119 +        final int indxLo = (byteCodes[offsetInstruction + 2] + 256) % 256;
   1.120 +        return (indxHi << 16) + indxLo;
   1.121 +    }
   1.122 +    
   1.123 +    private static int countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig) {
   1.124 +        int cnt = 0;
   1.125 +        int i = 0;
   1.126 +        Boolean count = null;
   1.127 +        while (i < descriptor.length()) {
   1.128 +            char ch = descriptor.charAt(i++);
   1.129 +            switch (ch) {
   1.130 +                case '(':
   1.131 +                    count = true;
   1.132 +                    continue;
   1.133 +                case ')':
   1.134 +                    count = false;
   1.135 +                    continue;
   1.136 +                case 'B': 
   1.137 +                case 'C': 
   1.138 +                case 'D': 
   1.139 +                case 'F': 
   1.140 +                case 'I': 
   1.141 +                case 'J': 
   1.142 +                case 'S': 
   1.143 +                case 'Z': 
   1.144 +                    if (count) {
   1.145 +                        cnt++;
   1.146 +                        sig.append(ch);
   1.147 +                    } else {
   1.148 +                        hasReturnType[0] = true;
   1.149 +                        sig.insert(0, ch);
   1.150 +                    }
   1.151 +                    continue;
   1.152 +                case 'V': 
   1.153 +                    assert !count;
   1.154 +                    hasReturnType[0] = false;
   1.155 +                    sig.insert(0, 'V');
   1.156 +                    continue;
   1.157 +                case 'L':
   1.158 +                    i = descriptor.indexOf(';', i);
   1.159 +                    if (count) {
   1.160 +                        cnt++;
   1.161 +                    } else {
   1.162 +                        hasReturnType[0] = true;
   1.163 +                    }
   1.164 +                    continue;
   1.165 +                case '[':
   1.166 +                    //arrays++;
   1.167 +                    continue;
   1.168 +                default:
   1.169 +                    break; // invalid character
   1.170 +            }
   1.171 +        }
   1.172 +        return cnt;
   1.173      }
   1.174  }
     2.1 --- a/src/test/java/org/apidesign/java4browser/StaticMethod.java	Mon Aug 27 06:12:35 2012 +0200
     2.2 +++ b/src/test/java/org/apidesign/java4browser/StaticMethod.java	Mon Aug 27 13:16:29 2012 +0200
     2.3 @@ -37,4 +37,11 @@
     2.4      public static int mix(int a, long b, byte c, double d) {
     2.5          return (int)((b / a + c) * d);
     2.6      }
     2.7 +    public static long factRec(int n) {
     2.8 +        if (n <= 1) {
     2.9 +            return 1;
    2.10 +        } else {
    2.11 +            return n * factRec(n - 1);
    2.12 +        }
    2.13 +    }
    2.14  }
     3.1 --- a/src/test/java/org/apidesign/java4browser/StaticMethodTest.java	Mon Aug 27 06:12:35 2012 +0200
     3.2 +++ b/src/test/java/org/apidesign/java4browser/StaticMethodTest.java	Mon Aug 27 13:16:29 2012 +0200
     3.3 @@ -74,6 +74,14 @@
     3.4              2, 10l, 5, 2.0
     3.5          );
     3.6      }
     3.7 +    @Test public void factRec() throws Exception {
     3.8 +        assertExec(
     3.9 +            "Factorial of 5 is 120",
    3.10 +            "org_apidesign_java4browser_StaticMethod_factRecJI", 
    3.11 +            Double.valueOf(120),
    3.12 +            5
    3.13 +        );
    3.14 +    }
    3.15      
    3.16      private static void assertExec(String msg, String methodName, Object expRes, Object... args) throws Exception {
    3.17          StringBuilder sb = new StringBuilder();