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();