Initial version of the Bytecode to JavaScript translator
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 27 Aug 2012 05:08:30 +0200
changeset 071aab30ab2b7
child 1 48b1dce93691
Initial version of the Bytecode to JavaScript translator
pom.xml
src/header.txt
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 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/pom.xml	Mon Aug 27 05:08:30 2012 +0200
     1.3 @@ -0,0 +1,86 @@
     1.4 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     1.5 +  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     1.6 +  <modelVersion>4.0.0</modelVersion>
     1.7 +
     1.8 +  <groupId>org.apidesign</groupId>
     1.9 +  <artifactId>java4browser</artifactId>
    1.10 +  <version>0.1-SNAPSHOT</version>
    1.11 +  <packaging>jar</packaging>
    1.12 +
    1.13 +  <name>java4browser</name>
    1.14 +  <url>http://java4browser.apidesign.org</url>
    1.15 +
    1.16 +  <properties>
    1.17 +    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    1.18 +    <author.name>Jaroslav Tulach</author.name>
    1.19 +    <author.email>jaroslav.tulach@apidesign.org</author.email>
    1.20 +  </properties>
    1.21 +  
    1.22 +  <repositories>
    1.23 +      <repository>
    1.24 +          <id>netbeans</id>
    1.25 +          <name>NetBeans</name>
    1.26 +          <url>http://bits.netbeans.org/maven2/</url>
    1.27 +      </repository>
    1.28 +  </repositories>
    1.29 +  <pluginRepositories>
    1.30 +    <pluginRepository>
    1.31 +        <id>mc-release</id>
    1.32 +        <name>Local Maven repository of releases</name>
    1.33 +        <url>http://mc-repo.googlecode.com/svn/maven2/releases</url>
    1.34 +        <snapshots>
    1.35 +            <enabled>false</enabled>
    1.36 +        </snapshots>
    1.37 +        <releases>
    1.38 +            <enabled>true</enabled>
    1.39 +        </releases>
    1.40 +    </pluginRepository>
    1.41 +  </pluginRepositories>
    1.42 +  <scm>
    1.43 +      <connection>scm:hg:http://source.apidesign.org/hg/quoridor</connection>
    1.44 +      <url>http://source.apidesign.org/hg/quoridor</url>
    1.45 +  </scm>
    1.46 +  <licenses>
    1.47 +    <license>
    1.48 +        <name>GPL-2.0</name>
    1.49 +        <url>http://opensource.org/licenses/GPL-2.0</url>
    1.50 +        <distribution>repo</distribution>
    1.51 +    </license>
    1.52 +  </licenses>
    1.53 +  <organization>
    1.54 +      <name>API Design</name>
    1.55 +      <url>http://apidesign.org</url>
    1.56 +  </organization>
    1.57 +    <build>
    1.58 +        <plugins>
    1.59 +            <plugin>
    1.60 +                <groupId>com.mycila.maven-license-plugin</groupId>
    1.61 +                <artifactId>maven-license-plugin</artifactId>
    1.62 +                <version>1.9.0</version>
    1.63 +                <configuration>
    1.64 +                    <header>src/header.txt</header>
    1.65 +                </configuration>
    1.66 +            </plugin>
    1.67 +        </plugins>
    1.68 +    </build>
    1.69 +  <dependencies>
    1.70 +    <dependency>
    1.71 +      <groupId>org.testng</groupId>
    1.72 +      <artifactId>testng</artifactId>
    1.73 +      <version>6.7</version>
    1.74 +      <scope>test</scope>
    1.75 +      <exclusions>
    1.76 +        <exclusion>
    1.77 +          <artifactId>junit</artifactId>
    1.78 +          <groupId>junit</groupId>
    1.79 +        </exclusion>
    1.80 +      </exclusions>
    1.81 +    </dependency>
    1.82 +    <dependency>
    1.83 +      <groupId>org.netbeans.api</groupId>
    1.84 +      <artifactId>org-netbeans-modules-classfile</artifactId>
    1.85 +      <version>RELEASE72</version>
    1.86 +      <type>jar</type>
    1.87 +    </dependency>
    1.88 +  </dependencies>
    1.89 +</project>
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/src/header.txt	Mon Aug 27 05:08:30 2012 +0200
     2.3 @@ -0,0 +1,15 @@
     2.4 +Java 4 Browser Bytecode Translator
     2.5 +Copyright (C) 2012-${year} Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     2.6 +
     2.7 +This program is free software: you can redistribute it and/or modify
     2.8 +it under the terms of the GNU General Public License as published by
     2.9 +the Free Software Foundation, version 2 of the License.
    2.10 +
    2.11 +This program is distributed in the hope that it will be useful,
    2.12 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.13 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.14 +GNU General Public License for more details.
    2.15 +
    2.16 +You should have received a copy of the GNU General Public License
    2.17 +along with this program. Look for COPYING file in the top folder.
    2.18 +If not, see http://opensource.org/licenses/GPL-2.0.
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java	Mon Aug 27 05:08:30 2012 +0200
     3.3 @@ -0,0 +1,121 @@
     3.4 +/*
     3.5 +Java 4 Browser Bytecode Translator
     3.6 +Copyright (C) 2012-2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 +
     3.8 +This program is free software: you can redistribute it and/or modify
     3.9 +it under the terms of the GNU General Public License as published by
    3.10 +the Free Software Foundation, version 2 of the License.
    3.11 +
    3.12 +This program is distributed in the hope that it will be useful,
    3.13 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 +GNU General Public License for more details.
    3.16 +
    3.17 +You should have received a copy of the GNU General Public License
    3.18 +along with this program. Look for COPYING file in the top folder.
    3.19 +If not, see http://opensource.org/licenses/GPL-2.0.
    3.20 +*/
    3.21 +package org.apidesign.java4browser;
    3.22 +
    3.23 +import java.io.IOException;
    3.24 +import java.io.InputStream;
    3.25 +import java.util.List;
    3.26 +import org.netbeans.modules.classfile.ByteCodes;
    3.27 +import org.netbeans.modules.classfile.ClassFile;
    3.28 +import org.netbeans.modules.classfile.Code;
    3.29 +import org.netbeans.modules.classfile.Method;
    3.30 +import org.netbeans.modules.classfile.Parameter;
    3.31 +
    3.32 +/** Translator of the code inside class files to JavaScript.
    3.33 + *
    3.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.35 + */
    3.36 +public final class ByteCodeToJavaScript {
    3.37 +    private final ClassFile jc;
    3.38 +    private final Appendable out;
    3.39 +
    3.40 +    private ByteCodeToJavaScript(ClassFile jc, Appendable out) {
    3.41 +        this.jc = jc;
    3.42 +        this.out = out;
    3.43 +    }
    3.44 +    
    3.45 +    /** Converts a given class file to a JavaScript version.
    3.46 +     * @param fileName the name of the file we are reading
    3.47 +     * @param classFile input stream with code of the .class file
    3.48 +     * @param out a {@link StringBuilder} or similar to generate the output to
    3.49 +     * @throws IOException if something goes wrong during read or write or translating
    3.50 +     */
    3.51 +    public static void compile(String fileName, InputStream classFile, Appendable out) throws IOException {
    3.52 +        ClassFile jc = new ClassFile(classFile, true);
    3.53 +        ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(jc, out);
    3.54 +        for (Method m : jc.getMethods()) {
    3.55 +            if (m.isStatic()) {
    3.56 +                compiler.generateStaticMethod(m);
    3.57 +            }
    3.58 +        }
    3.59 +    }
    3.60 +    private void generateStaticMethod(Method m) throws IOException {
    3.61 +        out.append("function ").append(
    3.62 +            jc.getName().getExternalName().replace('.', '_')
    3.63 +        ).append('_').append(
    3.64 +            m.getName()
    3.65 +        );
    3.66 +        out.append(m.getReturnType());
    3.67 +        List<Parameter> args = m.getParameters();
    3.68 +        for (Parameter t : args) {
    3.69 +            out.append(t.getDescriptor());
    3.70 +        }
    3.71 +        out.append('(');
    3.72 +        String space = "";
    3.73 +        for (int i = 0; i < args.size(); i++) {
    3.74 +            out.append(space);
    3.75 +            out.append("arg").append(String.valueOf(i));
    3.76 +            space = ",";
    3.77 +        }
    3.78 +        out.append(") {").append("\n  var ");
    3.79 +        final Code code = m.getCode();
    3.80 +        int len = code.getMaxLocals();
    3.81 +        space = "";
    3.82 +        for (int i = 0; i < len; i++) {
    3.83 +            out.append(space);
    3.84 +            out.append("var").append(String.valueOf(i));
    3.85 +            space = ",";
    3.86 +        }
    3.87 +        out.append(";\n  var stack = new Array(");
    3.88 +        out.append(Integer.toString(code.getMaxStack()));
    3.89 +        out.append(");\n");
    3.90 +        produceCode(code.getByteCodes());
    3.91 +        out.append(";\nreturn 1;");
    3.92 +        out.append("}");
    3.93 +    }
    3.94 +
    3.95 +    private void produceCode(byte[] byteCodes) throws IOException {
    3.96 +        for (int i = 0; i < byteCodes.length; i++) {
    3.97 +            int prev = i;
    3.98 +            out.append("  ");
    3.99 +            final int c = (byteCodes[i] + 256) % 256;
   3.100 +            switch (c) {
   3.101 +                case ByteCodes.bc_aload_0:
   3.102 +                case ByteCodes.bc_iload_0:
   3.103 +                    out.append("stack.push(arg0);");
   3.104 +                    break;
   3.105 +                case ByteCodes.bc_aload_1:
   3.106 +                case ByteCodes.bc_iload_1:
   3.107 +                    out.append("stack.push(arg1);");
   3.108 +                    break;
   3.109 +                case ByteCodes.bc_iadd:
   3.110 +                    out.append("stack.push(stack.pop() + stack.pop());");
   3.111 +                    break;
   3.112 +                case ByteCodes.bc_ireturn:
   3.113 +                    out.append("return stack.pop();");
   3.114 +            }
   3.115 +            out.append("/*");
   3.116 +            for (int j = prev; j <= i; j++) {
   3.117 +                out.append(" ");
   3.118 +                final int cc = (byteCodes[j] + 256) % 256;
   3.119 +                out.append(Integer.toString(cc));
   3.120 +            }
   3.121 +            out.append("*/\n");
   3.122 +        }
   3.123 +    }
   3.124 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/test/java/org/apidesign/java4browser/StaticMethod.java	Mon Aug 27 05:08:30 2012 +0200
     4.3 @@ -0,0 +1,28 @@
     4.4 +/*
     4.5 +Java 4 Browser Bytecode Translator
     4.6 +Copyright (C) 2012-2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4.7 +
     4.8 +This program is free software: you can redistribute it and/or modify
     4.9 +it under the terms of the GNU General Public License as published by
    4.10 +the Free Software Foundation, version 2 of the License.
    4.11 +
    4.12 +This program is distributed in the hope that it will be useful,
    4.13 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.14 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.15 +GNU General Public License for more details.
    4.16 +
    4.17 +You should have received a copy of the GNU General Public License
    4.18 +along with this program. Look for COPYING file in the top folder.
    4.19 +If not, see http://opensource.org/licenses/GPL-2.0.
    4.20 +*/
    4.21 +package org.apidesign.java4browser;
    4.22 +
    4.23 +/**
    4.24 + *
    4.25 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.26 + */
    4.27 +public class StaticMethod {
    4.28 +    public static int sum(int x, int y) {
    4.29 +        return x + y;
    4.30 +    }
    4.31 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/test/java/org/apidesign/java4browser/StaticMethodTest.java	Mon Aug 27 05:08:30 2012 +0200
     5.3 @@ -0,0 +1,57 @@
     5.4 +/*
     5.5 +Java 4 Browser Bytecode Translator
     5.6 +Copyright (C) 2012-2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 +
     5.8 +This program is free software: you can redistribute it and/or modify
     5.9 +it under the terms of the GNU General Public License as published by
    5.10 +the Free Software Foundation, version 2 of the License.
    5.11 +
    5.12 +This program is distributed in the hope that it will be useful,
    5.13 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 +GNU General Public License for more details.
    5.16 +
    5.17 +You should have received a copy of the GNU General Public License
    5.18 +along with this program. Look for COPYING file in the top folder.
    5.19 +If not, see http://opensource.org/licenses/GPL-2.0.
    5.20 +*/
    5.21 +package org.apidesign.java4browser;
    5.22 +
    5.23 +import java.io.IOException;
    5.24 +import java.io.InputStream;
    5.25 +import javax.script.Invocable;
    5.26 +import javax.script.ScriptEngine;
    5.27 +import javax.script.ScriptEngineManager;
    5.28 +import javax.script.ScriptException;
    5.29 +import static org.testng.Assert.*;
    5.30 +import org.testng.annotations.Test;
    5.31 +
    5.32 +/** Checks the basic behavior of the translator.
    5.33 + *
    5.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.35 + */
    5.36 +public class StaticMethodTest {
    5.37 +    @Test public void threePlusFour() throws Exception {
    5.38 +        Invocable i = compileClass("StaticMethod.class");
    5.39 +        
    5.40 +        Object ret = i.invokeFunction("org_apidesign_java4browser_StaticMethod_sumIII", 3, 4);
    5.41 +        assertEquals(ret, Double.valueOf(7), "Should be seven");
    5.42 +    }
    5.43 +
    5.44 +    static Invocable compileClass(String name) throws ScriptException, IOException {
    5.45 +        InputStream is = StaticMethodTest.class.getResourceAsStream(name);
    5.46 +        assertNotNull(is, "Class file found");
    5.47 +        StringBuilder sb = new StringBuilder();
    5.48 +        ByteCodeToJavaScript.compile(name, is, sb);
    5.49 +        ScriptEngineManager sem = new ScriptEngineManager();
    5.50 +        ScriptEngine js = sem.getEngineByExtension("js");
    5.51 +        try {
    5.52 +            Object res = js.eval(sb.toString());
    5.53 +            assertTrue(js instanceof Invocable, "It is invocable object: " + res);
    5.54 +            return (Invocable)js;
    5.55 +        } catch (ScriptException ex) {
    5.56 +            fail("Could not compile:\n" + sb, ex);
    5.57 +            return null;
    5.58 +        }
    5.59 +    }
    5.60 +}