jaroslav@0: /* jaroslav@0: Java 4 Browser Bytecode Translator jaroslav@0: Copyright (C) 2012-2012 Jaroslav Tulach jaroslav@0: jaroslav@0: This program is free software: you can redistribute it and/or modify jaroslav@0: it under the terms of the GNU General Public License as published by jaroslav@0: the Free Software Foundation, version 2 of the License. jaroslav@0: jaroslav@0: This program is distributed in the hope that it will be useful, jaroslav@0: but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@0: GNU General Public License for more details. jaroslav@0: jaroslav@0: You should have received a copy of the GNU General Public License jaroslav@0: along with this program. Look for COPYING file in the top folder. jaroslav@0: If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@0: */ jaroslav@0: package org.apidesign.java4browser; jaroslav@0: jaroslav@0: import java.io.IOException; jaroslav@0: import java.io.InputStream; jaroslav@0: import java.util.List; jaroslav@0: import org.netbeans.modules.classfile.ByteCodes; jaroslav@0: import org.netbeans.modules.classfile.ClassFile; jaroslav@0: import org.netbeans.modules.classfile.Code; jaroslav@0: import org.netbeans.modules.classfile.Method; jaroslav@0: import org.netbeans.modules.classfile.Parameter; jaroslav@0: jaroslav@0: /** Translator of the code inside class files to JavaScript. jaroslav@0: * jaroslav@0: * @author Jaroslav Tulach jaroslav@0: */ jaroslav@0: public final class ByteCodeToJavaScript { jaroslav@0: private final ClassFile jc; jaroslav@0: private final Appendable out; jaroslav@0: jaroslav@0: private ByteCodeToJavaScript(ClassFile jc, Appendable out) { jaroslav@0: this.jc = jc; jaroslav@0: this.out = out; jaroslav@0: } jaroslav@0: jaroslav@0: /** Converts a given class file to a JavaScript version. jaroslav@0: * @param fileName the name of the file we are reading jaroslav@0: * @param classFile input stream with code of the .class file jaroslav@0: * @param out a {@link StringBuilder} or similar to generate the output to jaroslav@0: * @throws IOException if something goes wrong during read or write or translating jaroslav@0: */ jaroslav@0: public static void compile(String fileName, InputStream classFile, Appendable out) throws IOException { jaroslav@0: ClassFile jc = new ClassFile(classFile, true); jaroslav@0: ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(jc, out); jaroslav@0: for (Method m : jc.getMethods()) { jaroslav@0: if (m.isStatic()) { jaroslav@0: compiler.generateStaticMethod(m); jaroslav@0: } jaroslav@0: } jaroslav@0: } jaroslav@0: private void generateStaticMethod(Method m) throws IOException { jaroslav@0: out.append("function ").append( jaroslav@0: jc.getName().getExternalName().replace('.', '_') jaroslav@0: ).append('_').append( jaroslav@0: m.getName() jaroslav@0: ); jaroslav@0: out.append(m.getReturnType()); jaroslav@0: List args = m.getParameters(); jaroslav@0: for (Parameter t : args) { jaroslav@0: out.append(t.getDescriptor()); jaroslav@0: } jaroslav@0: out.append('('); jaroslav@0: String space = ""; jaroslav@0: for (int i = 0; i < args.size(); i++) { jaroslav@0: out.append(space); jaroslav@0: out.append("arg").append(String.valueOf(i)); jaroslav@0: space = ","; jaroslav@0: } jaroslav@0: out.append(") {").append("\n var "); jaroslav@0: final Code code = m.getCode(); jaroslav@0: int len = code.getMaxLocals(); jaroslav@0: space = ""; jaroslav@0: for (int i = 0; i < len; i++) { jaroslav@0: out.append(space); jaroslav@0: out.append("var").append(String.valueOf(i)); jaroslav@0: space = ","; jaroslav@0: } jaroslav@0: out.append(";\n var stack = new Array("); jaroslav@0: out.append(Integer.toString(code.getMaxStack())); jaroslav@0: out.append(");\n"); jaroslav@0: produceCode(code.getByteCodes()); jaroslav@0: out.append(";\nreturn 1;"); jaroslav@0: out.append("}"); jaroslav@0: } jaroslav@0: jaroslav@0: private void produceCode(byte[] byteCodes) throws IOException { jaroslav@0: for (int i = 0; i < byteCodes.length; i++) { jaroslav@0: int prev = i; jaroslav@0: out.append(" "); jaroslav@0: final int c = (byteCodes[i] + 256) % 256; jaroslav@0: switch (c) { jaroslav@0: case ByteCodes.bc_aload_0: jaroslav@0: case ByteCodes.bc_iload_0: jaroslav@0: out.append("stack.push(arg0);"); jaroslav@0: break; jaroslav@0: case ByteCodes.bc_aload_1: jaroslav@0: case ByteCodes.bc_iload_1: jaroslav@0: out.append("stack.push(arg1);"); jaroslav@0: break; jaroslav@0: case ByteCodes.bc_iadd: jaroslav@0: out.append("stack.push(stack.pop() + stack.pop());"); jaroslav@0: break; jaroslav@0: case ByteCodes.bc_ireturn: jaroslav@0: out.append("return stack.pop();"); jaroslav@0: } jaroslav@0: out.append("/*"); jaroslav@0: for (int j = prev; j <= i; j++) { jaroslav@0: out.append(" "); jaroslav@0: final int cc = (byteCodes[j] + 256) % 256; jaroslav@0: out.append(Integer.toString(cc)); jaroslav@0: } jaroslav@0: out.append("*/\n"); jaroslav@0: } jaroslav@0: } jaroslav@0: }