2 * Back 2 Browser Bytecode Translator
3 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. Look for COPYING file in the top folder.
16 * If not, see http://opensource.org/licenses/GPL-2.0.
18 package org.apidesign.vm4brwsr;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import org.apidesign.javap.AnnotationParser;
23 import org.apidesign.javap.ClassData;
24 import org.apidesign.javap.FieldData;
25 import org.apidesign.javap.MethodData;
26 import org.apidesign.javap.StackMapIterator;
27 import static org.apidesign.javap.RuntimeConstants.*;
29 /** Translator of the code inside class files to JavaScript.
31 * @author Jaroslav Tulach <jtulach@netbeans.org>
33 public abstract class ByteCodeToJavaScript {
37 protected ByteCodeToJavaScript(Appendable out) {
41 /* Collects additional required resources.
43 * @param internalClassName classes that were referenced and should be loaded in order the
44 * generated JavaScript code works properly. The names are in internal
45 * JVM form so String is <code>java/lang/String</code>.
47 protected abstract boolean requireReference(String internalClassName);
50 * @param resourcePath name of resources to read
52 protected abstract void requireScript(String resourcePath);
54 /** Allows subclasses to redefine what field a function representing a
55 * class gets assigned. By default it returns the suggested name followed
56 * by <code>" = "</code>;
58 * @param className suggested name of the class
60 protected String assignClass(String className) {
61 return className + " = ";
65 * Converts a given class file to a JavaScript version.
67 * @param classFile input stream with code of the .class file
68 * @return the initialization code for this class, if any. Otherwise <code>null</code>
70 * @throws IOException if something goes wrong during read or write or translating
73 public String compile(InputStream classFile) throws IOException {
74 this.jc = new ClassData(classFile);
75 byte[] arrData = jc.findAnnotationData(true);
76 String[] arr = findAnnotation(arrData, jc,
77 "org.apidesign.bck2brwsr.core.ExtraJavaScript",
78 "resource", "processByteCode"
81 requireScript(arr[0]);
82 if ("0".equals(arr[1])) {
86 String[] proto = findAnnotation(arrData, jc,
87 "org.apidesign.bck2brwsr.core.JavaScriptPrototype",
88 "container", "prototype"
90 StringArray toInitilize = new StringArray();
91 final String className = className(jc);
92 out.append("\n\n").append(assignClass(className));
93 out.append("function CLS() {");
94 out.append("\n if (!CLS.prototype.$instOf_").append(className).append(") {");
95 for (FieldData v : jc.getFields()) {
97 out.append("\n CLS.").append(v.getName()).append(initField(v));
101 String sc = jc.getSuperClassName(); // with _
102 out.append("\n var pp = ").
103 append(sc.replace('/', '_')).append("(true);");
104 out.append("\n var p = CLS.prototype = pp;");
105 out.append("\n var c = p;");
106 out.append("\n var sprcls = pp.constructor.$class;");
108 out.append("\n var p = CLS.prototype = ").append(proto[1]).append(";");
109 out.append("\n var c = ").append(proto[0]).append(";");
110 out.append("\n var sprcls = null;");
112 for (MethodData m : jc.getMethods()) {
113 byte[] onlyArr = m.findAnnotationData(true);
114 String[] only = findAnnotation(onlyArr, jc,
115 "org.apidesign.bck2brwsr.core.JavaScriptOnly",
119 if (only[0] != null && only[1] != null) {
120 out.append("\n p.").append(only[0]).append(" = ")
121 .append(only[1]).append(";");
127 mn = generateStaticMethod("\n c.", m, toInitilize);
129 mn = generateInstanceMethod("\n c.", m);
131 byte[] runAnno = m.findAnnotationData(false);
132 if (runAnno != null) {
133 out.append("\n c.").append(mn).append(".anno = {");
134 generateAnno(jc, out, runAnno);
138 out.append("\n c.constructor = CLS;");
139 out.append("\n c.$instOf_").append(className).append(" = true;");
140 for (String superInterface : jc.getSuperInterfaces()) {
141 out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;");
143 out.append("\n CLS.$class = java_lang_Class(true);");
144 out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';");
145 out.append("\n CLS.$class.superclass = sprcls;");
146 out.append("\n CLS.$class.cnstr = CLS;");
147 byte[] classAnno = jc.findAnnotationData(false);
148 if (classAnno != null) {
149 out.append("\n CLS.$class.anno = {");
150 generateAnno(jc, out, classAnno);
154 out.append("\n if (arguments.length === 0) {");
155 out.append("\n if (!(this instanceof CLS)) {");
156 out.append("\n return new CLS();");
158 for (FieldData v : jc.getFields()) {
159 byte[] onlyArr = v.findAnnotationData(true);
160 String[] only = findAnnotation(onlyArr, jc,
161 "org.apidesign.bck2brwsr.core.JavaScriptOnly",
165 if (only[0] != null && only[1] != null) {
166 out.append("\n p.").append(only[0]).append(" = ")
167 .append(only[1]).append(";");
172 out.append("\n this.fld_").
173 append(v.getName()).append(initField(v));
176 out.append("\n return this;");
178 out.append("\n return arguments[0] ? new CLS() : CLS.prototype;");
180 StringBuilder sb = new StringBuilder();
181 for (String init : toInitilize.toArray()) {
182 sb.append("\n").append(init).append("();");
184 return sb.toString();
186 private String generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException {
187 String jsb = javaScriptBody(prefix, m, true);
191 final String mn = findMethodName(m, new StringBuilder());
192 if (mn.equals("class__V")) {
193 toInitilize.add(className(jc) + "(false)." + mn);
195 generateMethod(prefix, mn, m);
199 private String generateInstanceMethod(String prefix, MethodData m) throws IOException {
200 String jsb = javaScriptBody(prefix, m, false);
204 final String mn = findMethodName(m, new StringBuilder());
205 generateMethod(prefix, mn, m);
209 private void generateMethod(String prefix, String name, MethodData m)
211 final StackMapIterator stackMapIterator = m.createStackMapIterator();
212 final LocalsMapper lmapper =
213 new LocalsMapper(stackMapIterator.getArguments());
215 out.append(prefix).append(name).append(" = function(");
216 lmapper.outputArguments(out);
217 out.append(") {").append("\n");
219 final byte[] byteCodes = m.getCode();
220 if (byteCodes == null) {
221 out.append(" throw 'no code found for ")
222 .append(m.getInternalSig()).append("';\n");
227 final StackMapper smapper = new StackMapper();
229 final int maxLocals = m.getMaxLocals();
231 // TODO: generate only used local variables
232 for (int j = 0; j <= VarType.LAST; ++j) {
233 out.append("\n var ").append(Variable.getLocalVariable(j, 0));
234 for (int i = 1; i < maxLocals; ++i) {
236 out.append(Variable.getLocalVariable(j, i));
242 // maxStack includes two stack positions for every pushed long / double
243 // so this might generate more stack variables than we need
244 final int maxStack = m.getMaxStack();
246 // TODO: generate only used stack variables
247 for (int j = 0; j <= VarType.LAST; ++j) {
248 out.append("\n var ").append(Variable.getStackVariable(j, 0));
249 for (int i = 1; i < maxStack; ++i) {
251 out.append(Variable.getStackVariable(j, i));
257 int lastStackFrame = -1;
259 out.append("\n var gt = 0;\n for(;;) switch(gt) {\n");
260 for (int i = 0; i < byteCodes.length; i++) {
262 stackMapIterator.advanceTo(i);
263 if (lastStackFrame != stackMapIterator.getFrameIndex()) {
264 lastStackFrame = stackMapIterator.getFrameIndex();
265 lmapper.syncWithFrameLocals(stackMapIterator.getFrameLocals());
266 smapper.syncWithFrameStack(stackMapIterator.getFrameStack());
267 out.append(" case " + i).append(": ");
269 out.append(" /* " + i).append(" */ ");
271 final int c = readByte(byteCodes, i);
274 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(0));
277 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(0));
280 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(0));
283 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(0));
286 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(0));
289 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(1));
292 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(1));
295 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(1));
298 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(1));
301 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(1));
304 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(2));
307 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(2));
310 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(2));
313 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(2));
316 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(2));
319 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(3));
322 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(3));
325 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(3));
328 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(3));
331 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(3));
334 final int indx = readByte(byteCodes, ++i);
335 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(indx));
339 final int indx = readByte(byteCodes, ++i);
340 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(indx));
344 final int indx = readByte(byteCodes, ++i);
345 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(indx));
349 final int indx = readByte(byteCodes, ++i);
350 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(indx));
354 final int indx = readByte(byteCodes, ++i);
355 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(indx));
359 final int indx = readByte(byteCodes, ++i);
360 emit(out, "@1 = @2;", lmapper.setI(indx), smapper.popI());
364 final int indx = readByte(byteCodes, ++i);
365 emit(out, "@1 = @2;", lmapper.setL(indx), smapper.popL());
369 final int indx = readByte(byteCodes, ++i);
370 emit(out, "@1 = @2;", lmapper.setF(indx), smapper.popF());
374 final int indx = readByte(byteCodes, ++i);
375 emit(out, "@1 = @2;", lmapper.setD(indx), smapper.popD());
379 final int indx = readByte(byteCodes, ++i);
380 emit(out, "@1 = @2;", lmapper.setA(indx), smapper.popA());
384 emit(out, "@1 = @2;", lmapper.setA(0), smapper.popA());
387 emit(out, "@1 = @2;", lmapper.setI(0), smapper.popI());
390 emit(out, "@1 = @2;", lmapper.setL(0), smapper.popL());
393 emit(out, "@1 = @2;", lmapper.setF(0), smapper.popF());
396 emit(out, "@1 = @2;", lmapper.setD(0), smapper.popD());
399 emit(out, "@1 = @2;", lmapper.setA(1), smapper.popA());
402 emit(out, "@1 = @2;", lmapper.setI(1), smapper.popI());
405 emit(out, "@1 = @2;", lmapper.setL(1), smapper.popL());
408 emit(out, "@1 = @2;", lmapper.setF(1), smapper.popF());
411 emit(out, "@1 = @2;", lmapper.setD(1), smapper.popD());
414 emit(out, "@1 = @2;", lmapper.setA(2), smapper.popA());
417 emit(out, "@1 = @2;", lmapper.setI(2), smapper.popI());
420 emit(out, "@1 = @2;", lmapper.setL(2), smapper.popL());
423 emit(out, "@1 = @2;", lmapper.setF(2), smapper.popF());
426 emit(out, "@1 = @2;", lmapper.setD(2), smapper.popD());
429 emit(out, "@1 = @2;", lmapper.setA(3), smapper.popA());
432 emit(out, "@1 = @2;", lmapper.setI(3), smapper.popI());
435 emit(out, "@1 = @2;", lmapper.setL(3), smapper.popL());
438 emit(out, "@1 = @2;", lmapper.setF(3), smapper.popF());
441 emit(out, "@1 = @2;", lmapper.setD(3), smapper.popD());
444 emit(out, "@1 += @2;", smapper.getI(1), smapper.popI());
447 emit(out, "@1 += @2;", smapper.getL(1), smapper.popL());
450 emit(out, "@1 += @2;", smapper.getF(1), smapper.popF());
453 emit(out, "@1 += @2;", smapper.getD(1), smapper.popD());
456 emit(out, "@1 -= @2;", smapper.getI(1), smapper.popI());
459 emit(out, "@1 -= @2;", smapper.getL(1), smapper.popL());
462 emit(out, "@1 -= @2;", smapper.getF(1), smapper.popF());
465 emit(out, "@1 -= @2;", smapper.getD(1), smapper.popD());
468 emit(out, "@1 *= @2;", smapper.getI(1), smapper.popI());
471 emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL());
474 emit(out, "@1 *= @2;", smapper.getF(1), smapper.popF());
477 emit(out, "@1 *= @2;", smapper.getD(1), smapper.popD());
480 emit(out, "@1 = Math.floor(@1 / @2);",
481 smapper.getI(1), smapper.popI());
484 emit(out, "@1 = Math.floor(@1 / @2);",
485 smapper.getL(1), smapper.popL());
488 emit(out, "@1 /= @2;", smapper.getF(1), smapper.popF());
491 emit(out, "@1 /= @2;", smapper.getD(1), smapper.popD());
494 emit(out, "@1 %= @2;", smapper.getI(1), smapper.popI());
497 emit(out, "@1 %= @2;", smapper.getL(1), smapper.popL());
500 emit(out, "@1 %= @2;", smapper.getF(1), smapper.popF());
503 emit(out, "@1 %= @2;", smapper.getD(1), smapper.popD());
506 emit(out, "@1 &= @2;", smapper.getI(1), smapper.popI());
509 emit(out, "@1 &= @2;", smapper.getL(1), smapper.popL());
512 emit(out, "@1 |= @2;", smapper.getI(1), smapper.popI());
515 emit(out, "@1 |= @2;", smapper.getL(1), smapper.popL());
518 emit(out, "@1 ^= @2;", smapper.getI(1), smapper.popI());
521 emit(out, "@1 ^= @2;", smapper.getL(1), smapper.popL());
524 emit(out, "@1 = -@1;", smapper.getI(0));
527 emit(out, "@1 = -@1;", smapper.getL(0));
530 emit(out, "@1 = -@1;", smapper.getF(0));
533 emit(out, "@1 = -@1;", smapper.getD(0));
536 emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI());
539 emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI());
542 emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI());
545 emit(out, "@1 >>= @2;", smapper.getL(1), smapper.popI());
548 emit(out, "@1 >>>= @2;", smapper.getI(1), smapper.popI());
551 emit(out, "@1 >>>= @2;", smapper.getL(1), smapper.popI());
554 final int varIndx = readByte(byteCodes, ++i);
555 final int incrBy = byteCodes[++i];
557 emit(out, "@1++;", lmapper.getI(varIndx));
559 emit(out, "@1 += @2;",
560 lmapper.getI(varIndx),
561 Integer.toString(incrBy));
566 emit(out, "return;");
569 emit(out, "return @1;", smapper.popI());
572 emit(out, "return @1;", smapper.popL());
575 emit(out, "return @1;", smapper.popF());
578 emit(out, "return @1;", smapper.popD());
581 emit(out, "return @1;", smapper.popA());
584 emit(out, "@2 = @1;", smapper.popI(), smapper.pushL());
587 emit(out, "@2 = @1;", smapper.popI(), smapper.pushF());
590 emit(out, "@2 = @1;", smapper.popI(), smapper.pushD());
593 emit(out, "@2 = @1;", smapper.popL(), smapper.pushI());
597 emit(out, "@2 = @1;", smapper.popL(), smapper.pushF());
600 emit(out, "@2 = @1;", smapper.popL(), smapper.pushD());
603 emit(out, "@2 = @1;", smapper.popF(), smapper.pushD());
606 emit(out, "@2 = @1;", smapper.popD(), smapper.pushF());
609 emit(out, "@2 = Math.floor(@1);",
610 smapper.popF(), smapper.pushI());
613 emit(out, "@2 = Math.floor(@1);",
614 smapper.popF(), smapper.pushL());
617 emit(out, "@2 = Math.floor(@1);",
618 smapper.popD(), smapper.pushI());
621 emit(out, "@2 = Math.floor(@1);",
622 smapper.popD(), smapper.pushL());
627 out.append("/* number conversion */");
629 case opc_aconst_null:
630 emit(out, "@1 = null;", smapper.pushA());
633 emit(out, "@1 = -1;", smapper.pushI());
636 emit(out, "@1 = 0;", smapper.pushI());
639 emit(out, "@1 = 0;", smapper.pushD());
642 emit(out, "@1 = 0;", smapper.pushL());
645 emit(out, "@1 = 0;", smapper.pushF());
648 emit(out, "@1 = 1;", smapper.pushI());
651 emit(out, "@1 = 1;", smapper.pushL());
654 emit(out, "@1 = 1;", smapper.pushF());
657 emit(out, "@1 = 1;", smapper.pushD());
660 emit(out, "@1 = 2;", smapper.pushI());
663 emit(out, "@1 = 2;", smapper.pushF());
666 emit(out, "@1 = 3;", smapper.pushI());
669 emit(out, "@1 = 4;", smapper.pushI());
672 emit(out, "@1 = 5;", smapper.pushI());
675 int indx = readByte(byteCodes, ++i);
676 String v = encodeConstant(indx);
677 int type = VarType.fromConstantType(jc.getTag(indx));
678 emit(out, "@1 = @2;", smapper.pushT(type), v);
683 int indx = readIntArg(byteCodes, i);
685 String v = encodeConstant(indx);
686 int type = VarType.fromConstantType(jc.getTag(indx));
687 emit(out, "@1 = @2;", smapper.pushT(type), v);
691 emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
692 smapper.popL(), smapper.popL(), smapper.pushI());
696 emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
697 smapper.popF(), smapper.popF(), smapper.pushI());
701 emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
702 smapper.popD(), smapper.popD(), smapper.pushI());
705 i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
709 i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
713 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
717 int indx = i + readIntArg(byteCodes, i);
718 emit(out, "if (@1 == 0) { gt = @2; continue; }",
719 smapper.popI(), Integer.toString(indx));
724 int indx = i + readIntArg(byteCodes, i);
725 emit(out, "if (@1 != 0) { gt = @2; continue; }",
726 smapper.popI(), Integer.toString(indx));
731 int indx = i + readIntArg(byteCodes, i);
732 emit(out, "if (@1 < 0) { gt = @2; continue; }",
733 smapper.popI(), Integer.toString(indx));
738 int indx = i + readIntArg(byteCodes, i);
739 emit(out, "if (@1 <= 0) { gt = @2; continue; }",
740 smapper.popI(), Integer.toString(indx));
745 int indx = i + readIntArg(byteCodes, i);
746 emit(out, "if (@1 > 0) { gt = @2; continue; }",
747 smapper.popI(), Integer.toString(indx));
752 int indx = i + readIntArg(byteCodes, i);
753 emit(out, "if (@1 >= 0) { gt = @2; continue; }",
754 smapper.popI(), Integer.toString(indx));
758 case opc_ifnonnull: {
759 int indx = i + readIntArg(byteCodes, i);
760 emit(out, "if (@1 !== null) { gt = @2; continue; }",
761 smapper.popA(), Integer.toString(indx));
766 int indx = i + readIntArg(byteCodes, i);
767 emit(out, "if (@1 === null) { gt = @2; continue; }",
768 smapper.popA(), Integer.toString(indx));
773 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
777 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
781 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
785 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
789 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
793 int indx = i + readIntArg(byteCodes, i);
794 emit(out, "gt = @1; continue;", Integer.toString(indx));
798 case opc_lookupswitch: {
799 int table = i / 4 * 4 + 4;
800 int dflt = i + readInt4(byteCodes, table);
802 int n = readInt4(byteCodes, table);
804 out.append("switch (").append(smapper.popI()).append(") {\n");
806 int cnstnt = readInt4(byteCodes, table);
808 int offset = i + readInt4(byteCodes, table);
810 out.append(" case " + cnstnt).append(": gt = " + offset).append("; continue;\n");
812 out.append(" default: gt = " + dflt).append("; continue;\n}");
816 case opc_tableswitch: {
817 int table = i / 4 * 4 + 4;
818 int dflt = i + readInt4(byteCodes, table);
820 int low = readInt4(byteCodes, table);
822 int high = readInt4(byteCodes, table);
824 out.append("switch (").append(smapper.popI()).append(") {\n");
825 while (low <= high) {
826 int offset = i + readInt4(byteCodes, table);
828 out.append(" case " + low).append(": gt = " + offset).append("; continue;\n");
831 out.append(" default: gt = " + dflt).append("; continue;\n}");
835 case opc_invokeinterface: {
836 i = invokeVirtualMethod(byteCodes, i, smapper) + 2;
839 case opc_invokevirtual:
840 i = invokeVirtualMethod(byteCodes, i, smapper);
842 case opc_invokespecial:
843 i = invokeStaticMethod(byteCodes, i, smapper, false);
845 case opc_invokestatic:
846 i = invokeStaticMethod(byteCodes, i, smapper, true);
849 int indx = readIntArg(byteCodes, i);
850 String ci = jc.getClassName(indx);
851 emit(out, "@1 = new @2;",
852 smapper.pushA(), ci.replace('/', '_'));
858 ++i; // skip type of array
859 emit(out, "@2 = new Array(@1).fillNulls();",
860 smapper.popI(), smapper.pushA());
863 i += 2; // skip type of array
864 emit(out, "@2 = new Array(@1).fillNulls();",
865 smapper.popI(), smapper.pushA());
867 case opc_multianewarray: {
869 int dim = readByte(byteCodes, ++i);
870 out.append("{ var a0 = new Array(").append(smapper.popI())
871 .append(").fillNulls();");
872 for (int d = 1; d < dim; d++) {
873 out.append("\n var l" + d).append(" = ")
874 .append(smapper.popI()).append(';');
875 out.append("\n for (var i" + d).append (" = 0; i" + d).
876 append(" < a" + (d - 1)).
877 append(".length; i" + d).append("++) {");
878 out.append("\n var a" + d).
879 append (" = new Array(l" + d).append(").fillNulls();");
880 out.append("\n a" + (d - 1)).append("[i" + d).append("] = a" + d).
883 for (int d = 1; d < dim; d++) {
886 out.append("\n").append(smapper.pushA()).append(" = a0; }");
889 case opc_arraylength:
890 emit(out, "@2 = @1.length;", smapper.popA(), smapper.pushI());
893 emit(out, "@3[@2] = @1;",
894 smapper.popL(), smapper.popI(), smapper.popA());
897 emit(out, "@3[@2] = @1;",
898 smapper.popF(), smapper.popI(), smapper.popA());
901 emit(out, "@3[@2] = @1;",
902 smapper.popD(), smapper.popI(), smapper.popA());
905 emit(out, "@3[@2] = @1;",
906 smapper.popA(), smapper.popI(), smapper.popA());
912 emit(out, "@3[@2] = @1;",
913 smapper.popI(), smapper.popI(), smapper.popA());
916 emit(out, "@3 = @2[@1];",
917 smapper.popI(), smapper.popA(), smapper.pushL());
920 emit(out, "@3 = @2[@1];",
921 smapper.popI(), smapper.popA(), smapper.pushF());
924 emit(out, "@3 = @2[@1];",
925 smapper.popI(), smapper.popA(), smapper.pushD());
928 emit(out, "@3 = @2[@1];",
929 smapper.popI(), smapper.popA(), smapper.pushA());
935 emit(out, "@3 = @2[@1];",
936 smapper.popI(), smapper.popA(), smapper.pushI());
941 out.append("/* pop */");
944 final Variable v = smapper.get(0);
945 emit(out, "@1 = @2;", smapper.pushT(v.getType()), v);
949 if (smapper.get(0).isCategory2()) {
950 final Variable v = smapper.get(0);
951 emit(out, "@1 = @2;", smapper.pushT(v.getType()), v);
953 final Variable v1 = smapper.get(0);
954 final Variable v2 = smapper.get(1);
955 emit(out, "{ @1 = @2; @3 = @4; }",
956 smapper.pushT(v2.getType()), v2,
957 smapper.pushT(v1.getType()), v1);
962 final Variable vi1 = smapper.pop();
963 final Variable vi2 = smapper.pop();
964 final Variable vo3 = smapper.pushT(vi1.getType());
965 final Variable vo2 = smapper.pushT(vi2.getType());
966 final Variable vo1 = smapper.pushT(vi1.getType());
968 emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }",
969 vo1, vi1, vo2, vi2, vo3, vo1);
973 if (smapper.get(1).isCategory2()) {
974 final Variable vi1 = smapper.pop();
975 final Variable vi2 = smapper.pop();
976 final Variable vo3 = smapper.pushT(vi1.getType());
977 final Variable vo2 = smapper.pushT(vi2.getType());
978 final Variable vo1 = smapper.pushT(vi1.getType());
980 emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }",
981 vo1, vi1, vo2, vi2, vo3, vo1);
983 final Variable vi1 = smapper.pop();
984 final Variable vi2 = smapper.pop();
985 final Variable vi3 = smapper.pop();
986 final Variable vo4 = smapper.pushT(vi1.getType());
987 final Variable vo3 = smapper.pushT(vi3.getType());
988 final Variable vo2 = smapper.pushT(vi2.getType());
989 final Variable vo1 = smapper.pushT(vi1.getType());
991 emit(out, "{ @1 = @2; @3 = @4; @5 = @6; @7 = @8; }",
992 vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1);
997 emit(out, "@1 = @2;",
998 smapper.pushI(), Integer.toString(byteCodes[++i]));
1001 emit(out, "@1 = @2;",
1003 Integer.toString(readIntArg(byteCodes, i)));
1006 case opc_getfield: {
1007 int indx = readIntArg(byteCodes, i);
1008 String[] fi = jc.getFieldInfoName(indx);
1009 final int type = VarType.fromFieldType(fi[2].charAt(0));
1010 emit(out, "@2 = @1.fld_@3;",
1011 smapper.popA(), smapper.pushT(type), fi[1]);
1015 case opc_getstatic: {
1016 int indx = readIntArg(byteCodes, i);
1017 String[] fi = jc.getFieldInfoName(indx);
1018 final int type = VarType.fromFieldType(fi[2].charAt(0));
1019 emit(out, "@1 = @2.@3;",
1020 smapper.pushT(type), fi[0].replace('/', '_'), fi[1]);
1022 addReference(fi[0]);
1025 case opc_putfield: {
1026 int indx = readIntArg(byteCodes, i);
1027 String[] fi = jc.getFieldInfoName(indx);
1028 final int type = VarType.fromFieldType(fi[2].charAt(0));
1029 emit(out, "@2.fld_@3 = @1;",
1030 smapper.popT(type), smapper.popA(), fi[1]);
1034 case opc_putstatic: {
1035 int indx = readIntArg(byteCodes, i);
1036 String[] fi = jc.getFieldInfoName(indx);
1037 final int type = VarType.fromFieldType(fi[2].charAt(0));
1038 emit(out, "@1.@2 = @3;",
1039 fi[0].replace('/', '_'), fi[1], smapper.popT(type));
1041 addReference(fi[0]);
1044 case opc_checkcast: {
1045 int indx = readIntArg(byteCodes, i);
1046 final String type = jc.getClassName(indx);
1047 if (!type.startsWith("[")) {
1048 // no way to check arrays right now
1049 // XXX proper exception
1050 emit(out, "if (@1.$instOf_@2 != 1) throw {};",
1051 smapper.getA(0), type.replace('/', '_'));
1056 case opc_instanceof: {
1057 int indx = readIntArg(byteCodes, i);
1058 final String type = jc.getClassName(indx);
1059 emit(out, "@2 = @1.$instOf_@3 ? 1 : 0;",
1060 smapper.popA(), smapper.pushI(), type.replace('/', '_'));
1065 final Variable v = smapper.popA();
1068 emit(out, "{ @1 = @2; throw @2; }",
1069 smapper.pushA(), v);
1073 case opc_monitorenter: {
1074 out.append("/* monitor enter */");
1079 case opc_monitorexit: {
1080 out.append("/* monitor exit */");
1086 emit(out, "throw 'unknown bytecode @1';",
1087 Integer.toString(c));
1091 for (int j = prev; j <= i; j++) {
1093 final int cc = readByte(byteCodes, j);
1094 out.append(Integer.toString(cc));
1102 private int generateIf(byte[] byteCodes, int i,
1103 final Variable v2, final Variable v1,
1104 final String test) throws IOException {
1105 int indx = i + readIntArg(byteCodes, i);
1106 out.append("if (").append(v1)
1107 .append(' ').append(test).append(' ')
1108 .append(v2).append(") { gt = " + indx)
1109 .append("; continue; }");
1113 private int readIntArg(byte[] byteCodes, int offsetInstruction) {
1114 final int indxHi = byteCodes[offsetInstruction + 1] << 8;
1115 final int indxLo = byteCodes[offsetInstruction + 2];
1116 return (indxHi & 0xffffff00) | (indxLo & 0xff);
1118 private int readInt4(byte[] byteCodes, int offsetInstruction) {
1119 final int d = byteCodes[offsetInstruction + 0] << 24;
1120 final int c = byteCodes[offsetInstruction + 1] << 16;
1121 final int b = byteCodes[offsetInstruction + 2] << 8;
1122 final int a = byteCodes[offsetInstruction + 3];
1123 return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff);
1125 private int readByte(byte[] byteCodes, int offsetInstruction) {
1126 return byteCodes[offsetInstruction] & 0xff;
1129 private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) {
1131 Boolean count = null;
1132 boolean array = false;
1134 int firstPos = sig.length();
1135 while (i < descriptor.length()) {
1136 char ch = descriptor.charAt(i++);
1157 if (ch == 'J' || ch == 'D') {
1163 sig.insert(firstPos, ch);
1165 returnType[0] = '[';
1166 sig.insert(firstPos, "_3");
1175 returnType[0] = 'V';
1176 sig.insert(firstPos, 'V');
1179 int next = descriptor.indexOf(';', i);
1180 String realSig = mangleSig(descriptor, i - 1, next + 1);
1185 sig.append(realSig);
1188 sig.insert(firstPos, realSig);
1190 sig.insert(firstPos, "_3");
1192 returnType[0] = 'L';
1200 throw new IllegalStateException("Invalid char: " + ch);
1205 private static String mangleSig(String txt, int first, int last) {
1206 StringBuilder sb = new StringBuilder();
1207 for (int i = first; i < last; i++) {
1208 final char ch = txt.charAt(i);
1210 case '/': sb.append('_'); break;
1211 case '_': sb.append("_1"); break;
1212 case ';': sb.append("_2"); break;
1213 case '[': sb.append("_3"); break;
1214 default: sb.append(ch); break;
1217 return sb.toString();
1220 private static String findMethodName(MethodData m, StringBuilder cnt) {
1221 StringBuilder name = new StringBuilder();
1222 if ("<init>".equals(m.getName())) { // NOI18N
1223 name.append("cons"); // NOI18N
1224 } else if ("<clinit>".equals(m.getName())) { // NOI18N
1225 name.append("class"); // NOI18N
1227 name.append(m.getName());
1230 countArgs(m.getInternalSig(), new char[1], name, cnt);
1231 return name.toString();
1234 static String findMethodName(String[] mi, StringBuilder cnt, char[] returnType) {
1235 StringBuilder name = new StringBuilder();
1236 String descr = mi[2];//mi.getDescriptor();
1238 if ("<init>".equals(nm)) { // NOI18N
1239 name.append("cons"); // NOI18N
1243 countArgs(descr, returnType, name, cnt);
1244 return name.toString();
1247 private int invokeStaticMethod(byte[] byteCodes, int i, final StackMapper mapper, boolean isStatic)
1248 throws IOException {
1249 int methodIndex = readIntArg(byteCodes, i);
1250 String[] mi = jc.getFieldInfoName(methodIndex);
1251 char[] returnType = { 'V' };
1252 StringBuilder cnt = new StringBuilder();
1253 String mn = findMethodName(mi, cnt, returnType);
1255 final int numArguments = isStatic ? cnt.length() : cnt.length() + 1;
1256 final Variable[] vars = new Variable[numArguments];
1258 for (int j = numArguments - 1; j >= 0; --j) {
1259 vars[j] = mapper.pop();
1262 if (returnType[0] != 'V') {
1263 out.append(mapper.pushT(VarType.fromFieldType(returnType[0])))
1267 final String in = mi[0];
1268 out.append(in.replace('/', '_'));
1269 out.append("(false).");
1272 if (numArguments > 0) {
1273 out.append(vars[0]);
1274 for (int j = 1; j < numArguments; ++j) {
1276 out.append(vars[j]);
1284 private int invokeVirtualMethod(byte[] byteCodes, int i, final StackMapper mapper)
1285 throws IOException {
1286 int methodIndex = readIntArg(byteCodes, i);
1287 String[] mi = jc.getFieldInfoName(methodIndex);
1288 char[] returnType = { 'V' };
1289 StringBuilder cnt = new StringBuilder();
1290 String mn = findMethodName(mi, cnt, returnType);
1292 final int numArguments = cnt.length() + 1;
1293 final Variable[] vars = new Variable[numArguments];
1295 for (int j = numArguments - 1; j >= 0; --j) {
1296 vars[j] = mapper.pop();
1299 if (returnType[0] != 'V') {
1300 out.append(mapper.pushT(VarType.fromFieldType(returnType[0])))
1304 out.append(vars[0]).append('.');
1307 out.append(vars[0]);
1308 for (int j = 1; j < numArguments; ++j) {
1310 out.append(vars[j]);
1317 private void addReference(String cn) throws IOException {
1318 if (requireReference(cn)) {
1319 out.append(" /* needs ").append(cn).append(" */");
1323 private void outType(String d, StringBuilder out) {
1325 while (d.charAt(0) == '[') {
1329 if (d.charAt(0) == 'L') {
1330 assert d.charAt(d.length() - 1) == ';';
1331 out.append(d.replace('/', '_').substring(0, d.length() - 1));
1337 private String encodeConstant(int entryIndex) throws IOException {
1338 String[] classRef = { null };
1339 String s = jc.stringValue(entryIndex, classRef);
1340 if (classRef[0] != null) {
1341 addReference(classRef[0]);
1346 private String javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException {
1347 byte[] arr = m.findAnnotationData(true);
1351 final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;";
1352 class P extends AnnotationParser {
1358 String[] args = new String[30];
1362 protected void visitAttr(String type, String attr, String at, String value) {
1363 if (type.equals(jvmType)) {
1364 if ("body".equals(attr)) {
1366 } else if ("args".equals(attr)) {
1367 args[cnt++] = value;
1369 throw new IllegalArgumentException(attr);
1376 if (p.body == null) {
1379 StringBuilder cnt = new StringBuilder();
1380 final String mn = findMethodName(m, cnt);
1381 out.append(prefix).append(mn);
1382 out.append(" = function(");
1386 out.append(p.args[0]);
1393 for (int i = 0; i < cnt.length(); i++) {
1395 out.append(p.args[index]);
1399 out.append(") {").append("\n");
1401 out.append("\n}\n");
1404 private static String className(ClassData jc) {
1405 //return jc.getName().getInternalName().replace('/', '_');
1406 return jc.getClassName().replace('/', '_');
1409 private static String[] findAnnotation(
1410 byte[] arr, ClassData cd, final String className,
1411 final String... attrNames
1412 ) throws IOException {
1416 final String[] values = new String[attrNames.length];
1417 final boolean[] found = { false };
1418 final String jvmType = "L" + className.replace('.', '/') + ";";
1419 AnnotationParser ap = new AnnotationParser(false) {
1421 protected void visitAttr(String type, String attr, String at, String value) {
1422 if (type.equals(jvmType)) {
1424 for (int i = 0; i < attrNames.length; i++) {
1425 if (attrNames[i].equals(attr)) {
1434 return found[0] ? values : null;
1437 private CharSequence initField(FieldData v) {
1438 final String is = v.getInternalSig();
1439 if (is.length() == 1) {
1440 switch (is.charAt(0)) {
1446 case 'I': return " = 0;";
1448 case 'D': return " = 0.0;";
1450 throw new IllegalStateException(is);
1456 private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException {
1457 AnnotationParser ap = new AnnotationParser(true) {
1462 protected void visitAnnotationStart(String type) throws IOException {
1466 out.append('"').append(type).append("\" : {\n");
1471 protected void visitAnnotationEnd(String type) throws IOException {
1472 out.append("\n}\n");
1476 protected void visitAttr(String type, String attr, String attrType, String value)
1477 throws IOException {
1484 out.append(attr).append("__").append(attrType).append(" : ").append(value);
1490 private static void emit(final Appendable out,
1491 final String format,
1492 final CharSequence... params) throws IOException {
1493 final int length = format.length();
1496 int paramOffset = format.indexOf('@');
1497 while ((paramOffset != -1) && (paramOffset < (length - 1))) {
1498 final char paramChar = format.charAt(paramOffset + 1);
1499 if ((paramChar >= '1') && (paramChar <= '9')) {
1500 final int paramIndex = paramChar - '0' - 1;
1502 out.append(format, processed, paramOffset);
1503 out.append(params[paramIndex]);
1506 processed = paramOffset + 1;
1509 paramOffset = format.indexOf('@', paramOffset + 1);
1512 out.append(format, processed, length);