Added test for + - * operations in int32 arithmetic, updated JS generator to produce code for correct int32 arithmetic.
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 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 + " = ";
63 /* protected */ String accessClass(String classOperation) {
64 return classOperation;
68 * Converts a given class file to a JavaScript version.
70 * @param classFile input stream with code of the .class file
71 * @return the initialization code for this class, if any. Otherwise <code>null</code>
73 * @throws IOException if something goes wrong during read or write or translating
76 public String compile(InputStream classFile) throws IOException {
77 this.jc = new ClassData(classFile);
78 if (jc.getMajor_version() < 50) {
79 throw new IOException("Can't compile " + jc.getClassName() + ". Class file version " + jc.getMajor_version() + "."
80 + jc.getMinor_version() + " - recompile with -target 1.6 (at least)."
83 byte[] arrData = jc.findAnnotationData(true);
84 String[] arr = findAnnotation(arrData, jc,
85 "org.apidesign.bck2brwsr.core.ExtraJavaScript",
86 "resource", "processByteCode"
89 requireScript(arr[0]);
90 if ("0".equals(arr[1])) {
94 String[] proto = findAnnotation(arrData, jc,
95 "org.apidesign.bck2brwsr.core.JavaScriptPrototype",
96 "container", "prototype"
98 StringArray toInitilize = new StringArray();
99 final String className = className(jc);
100 out.append("\n\n").append(assignClass(className));
101 out.append("function CLS() {");
102 out.append("\n if (!CLS.prototype.$instOf_").append(className).append(") {");
103 for (FieldData v : jc.getFields()) {
105 out.append("\n CLS.").append(v.getName()).append(initField(v));
109 String sc = jc.getSuperClassName(); // with _
110 out.append("\n var pp = ").
111 append(accessClass(sc.replace('/', '_'))).append("(true);");
112 out.append("\n var p = CLS.prototype = pp;");
113 out.append("\n var c = p;");
114 out.append("\n var sprcls = pp.constructor.$class;");
116 out.append("\n var p = CLS.prototype = ").append(proto[1]).append(";");
117 if (proto[0] == null) {
120 out.append("\n var c = ").append(proto[0]).append(";");
121 out.append("\n var sprcls = null;");
123 for (MethodData m : jc.getMethods()) {
124 byte[] onlyArr = m.findAnnotationData(true);
125 String[] only = findAnnotation(onlyArr, jc,
126 "org.apidesign.bck2brwsr.core.JavaScriptOnly",
130 if (only[0] != null && only[1] != null) {
131 out.append("\n p.").append(only[0]).append(" = ")
132 .append(only[1]).append(";");
138 mn = generateStaticMethod("\n c.", m, toInitilize);
140 mn = generateInstanceMethod("\n c.", m);
142 byte[] runAnno = m.findAnnotationData(false);
143 if (runAnno != null) {
144 out.append("\n c.").append(mn).append(".anno = {");
145 generateAnno(jc, out, runAnno);
149 out.append("\n c.constructor = CLS;");
150 out.append("\n c.$instOf_").append(className).append(" = true;");
151 for (String superInterface : jc.getSuperInterfaces()) {
152 out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;");
154 out.append("\n CLS.$class = ");
155 out.append(accessClass("java_lang_Class(true);"));
156 out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';");
157 out.append("\n CLS.$class.superclass = sprcls;");
158 out.append("\n CLS.$class.cnstr = CLS;");
159 byte[] classAnno = jc.findAnnotationData(false);
160 if (classAnno != null) {
161 out.append("\n CLS.$class.anno = {");
162 generateAnno(jc, out, classAnno);
166 out.append("\n if (arguments.length === 0) {");
167 out.append("\n if (!(this instanceof CLS)) {");
168 out.append("\n return new CLS();");
170 for (FieldData v : jc.getFields()) {
171 byte[] onlyArr = v.findAnnotationData(true);
172 String[] only = findAnnotation(onlyArr, jc,
173 "org.apidesign.bck2brwsr.core.JavaScriptOnly",
177 if (only[0] != null && only[1] != null) {
178 out.append("\n p.").append(only[0]).append(" = ")
179 .append(only[1]).append(";");
184 out.append("\n this.fld_").
185 append(v.getName()).append(initField(v));
188 out.append("\n return this;");
190 out.append("\n return arguments[0] ? new CLS() : CLS.prototype;");
192 StringBuilder sb = new StringBuilder();
193 for (String init : toInitilize.toArray()) {
194 sb.append("\n").append(init).append("();");
196 return sb.toString();
198 private String generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException {
199 String jsb = javaScriptBody(prefix, m, true);
203 final String mn = findMethodName(m, new StringBuilder());
204 if (mn.equals("class__V")) {
205 toInitilize.add(accessClass(className(jc)) + "(false)." + mn);
207 generateMethod(prefix, mn, m);
211 private String generateInstanceMethod(String prefix, MethodData m) throws IOException {
212 String jsb = javaScriptBody(prefix, m, false);
216 final String mn = findMethodName(m, new StringBuilder());
217 generateMethod(prefix, mn, m);
221 private void generateMethod(String prefix, String name, MethodData m)
223 final StackMapIterator stackMapIterator = m.createStackMapIterator();
224 final LocalsMapper lmapper =
225 new LocalsMapper(stackMapIterator.getArguments());
227 out.append(prefix).append(name).append(" = function(");
228 lmapper.outputArguments(out);
229 out.append(") {").append("\n");
231 final byte[] byteCodes = m.getCode();
232 if (byteCodes == null) {
233 out.append(" throw 'no code found for ")
234 .append(m.getInternalSig()).append("';\n");
239 final StackMapper smapper = new StackMapper();
241 final int maxLocals = m.getMaxLocals();
243 // TODO: generate only used local variables
244 for (int j = 0; j <= VarType.LAST; ++j) {
245 out.append("\n var ").append(Variable.getLocalVariable(j, 0));
246 for (int i = 1; i < maxLocals; ++i) {
248 out.append(Variable.getLocalVariable(j, i));
254 // maxStack includes two stack positions for every pushed long / double
255 // so this might generate more stack variables than we need
256 final int maxStack = m.getMaxStack();
258 // TODO: generate only used stack variables
259 for (int j = 0; j <= VarType.LAST; ++j) {
260 out.append("\n var ").append(Variable.getStackVariable(j, 0));
261 for (int i = 1; i < maxStack; ++i) {
263 out.append(Variable.getStackVariable(j, i));
269 int lastStackFrame = -1;
271 out.append("\n var gt = 0;\n for(;;) switch(gt) {\n");
272 for (int i = 0; i < byteCodes.length; i++) {
274 stackMapIterator.advanceTo(i);
275 if (lastStackFrame != stackMapIterator.getFrameIndex()) {
276 lastStackFrame = stackMapIterator.getFrameIndex();
277 lmapper.syncWithFrameLocals(stackMapIterator.getFrameLocals());
278 smapper.syncWithFrameStack(stackMapIterator.getFrameStack());
279 out.append(" case " + i).append(": ");
281 out.append(" /* " + i).append(" */ ");
283 final int c = readByte(byteCodes, i);
286 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(0));
289 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(0));
292 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(0));
295 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(0));
298 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(0));
301 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(1));
304 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(1));
307 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(1));
310 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(1));
313 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(1));
316 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(2));
319 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(2));
322 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(2));
325 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(2));
328 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(2));
331 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(3));
334 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(3));
337 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(3));
340 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(3));
343 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(3));
346 final int indx = readByte(byteCodes, ++i);
347 emit(out, "@1 = @2;", smapper.pushI(), lmapper.getI(indx));
351 final int indx = readByte(byteCodes, ++i);
352 emit(out, "@1 = @2;", smapper.pushL(), lmapper.getL(indx));
356 final int indx = readByte(byteCodes, ++i);
357 emit(out, "@1 = @2;", smapper.pushF(), lmapper.getF(indx));
361 final int indx = readByte(byteCodes, ++i);
362 emit(out, "@1 = @2;", smapper.pushD(), lmapper.getD(indx));
366 final int indx = readByte(byteCodes, ++i);
367 emit(out, "@1 = @2;", smapper.pushA(), lmapper.getA(indx));
371 final int indx = readByte(byteCodes, ++i);
372 emit(out, "@1 = @2;", lmapper.setI(indx), smapper.popI());
376 final int indx = readByte(byteCodes, ++i);
377 emit(out, "@1 = @2;", lmapper.setL(indx), smapper.popL());
381 final int indx = readByte(byteCodes, ++i);
382 emit(out, "@1 = @2;", lmapper.setF(indx), smapper.popF());
386 final int indx = readByte(byteCodes, ++i);
387 emit(out, "@1 = @2;", lmapper.setD(indx), smapper.popD());
391 final int indx = readByte(byteCodes, ++i);
392 emit(out, "@1 = @2;", lmapper.setA(indx), smapper.popA());
396 emit(out, "@1 = @2;", lmapper.setA(0), smapper.popA());
399 emit(out, "@1 = @2;", lmapper.setI(0), smapper.popI());
402 emit(out, "@1 = @2;", lmapper.setL(0), smapper.popL());
405 emit(out, "@1 = @2;", lmapper.setF(0), smapper.popF());
408 emit(out, "@1 = @2;", lmapper.setD(0), smapper.popD());
411 emit(out, "@1 = @2;", lmapper.setA(1), smapper.popA());
414 emit(out, "@1 = @2;", lmapper.setI(1), smapper.popI());
417 emit(out, "@1 = @2;", lmapper.setL(1), smapper.popL());
420 emit(out, "@1 = @2;", lmapper.setF(1), smapper.popF());
423 emit(out, "@1 = @2;", lmapper.setD(1), smapper.popD());
426 emit(out, "@1 = @2;", lmapper.setA(2), smapper.popA());
429 emit(out, "@1 = @2;", lmapper.setI(2), smapper.popI());
432 emit(out, "@1 = @2;", lmapper.setL(2), smapper.popL());
435 emit(out, "@1 = @2;", lmapper.setF(2), smapper.popF());
438 emit(out, "@1 = @2;", lmapper.setD(2), smapper.popD());
441 emit(out, "@1 = @2;", lmapper.setA(3), smapper.popA());
444 emit(out, "@1 = @2;", lmapper.setI(3), smapper.popI());
447 emit(out, "@1 = @2;", lmapper.setL(3), smapper.popL());
450 emit(out, "@1 = @2;", lmapper.setF(3), smapper.popF());
453 emit(out, "@1 = @2;", lmapper.setD(3), smapper.popD());
456 emit(out, "@1 = (@1 + @2) | 0;", 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 = (@1 - @2) | 0;", 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 = (((@1 * (@2 >> 16)) << 16) + @1 * (@2 & 0xFFFF)) | 0;", smapper.getI(1), smapper.popI());
483 emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL());
486 emit(out, "@1 *= @2;", smapper.getF(1), smapper.popF());
489 emit(out, "@1 *= @2;", smapper.getD(1), smapper.popD());
492 emit(out, "@1 = Math.floor(@1 / @2);",
493 smapper.getI(1), smapper.popI());
496 emit(out, "@1 = Math.floor(@1 / @2);",
497 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.getF(1), smapper.popF());
515 emit(out, "@1 %= @2;", smapper.getD(1), smapper.popD());
518 emit(out, "@1 &= @2;", smapper.getI(1), smapper.popI());
521 emit(out, "@1 &= @2;", smapper.getL(1), smapper.popL());
524 emit(out, "@1 |= @2;", smapper.getI(1), smapper.popI());
527 emit(out, "@1 |= @2;", smapper.getL(1), smapper.popL());
530 emit(out, "@1 ^= @2;", smapper.getI(1), smapper.popI());
533 emit(out, "@1 ^= @2;", smapper.getL(1), smapper.popL());
536 emit(out, "@1 = -@1;", smapper.getI(0));
539 emit(out, "@1 = -@1;", smapper.getL(0));
542 emit(out, "@1 = -@1;", smapper.getF(0));
545 emit(out, "@1 = -@1;", smapper.getD(0));
548 emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI());
551 emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI());
554 emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI());
557 emit(out, "@1 >>= @2;", smapper.getL(1), smapper.popI());
560 emit(out, "@1 >>>= @2;", smapper.getI(1), smapper.popI());
563 emit(out, "@1 >>>= @2;", smapper.getL(1), smapper.popI());
566 final int varIndx = readByte(byteCodes, ++i);
567 final int incrBy = byteCodes[++i];
569 emit(out, "@1++;", lmapper.getI(varIndx));
571 emit(out, "@1 += @2;",
572 lmapper.getI(varIndx),
573 Integer.toString(incrBy));
578 emit(out, "return;");
581 emit(out, "return @1;", smapper.popI());
584 emit(out, "return @1;", smapper.popL());
587 emit(out, "return @1;", smapper.popF());
590 emit(out, "return @1;", smapper.popD());
593 emit(out, "return @1;", smapper.popA());
596 emit(out, "@2 = @1;", smapper.popI(), smapper.pushL());
599 emit(out, "@2 = @1;", smapper.popI(), smapper.pushF());
602 emit(out, "@2 = @1;", smapper.popI(), smapper.pushD());
605 emit(out, "@2 = @1;", smapper.popL(), smapper.pushI());
609 emit(out, "@2 = @1;", smapper.popL(), smapper.pushF());
612 emit(out, "@2 = @1;", smapper.popL(), smapper.pushD());
615 emit(out, "@2 = @1;", smapper.popF(), smapper.pushD());
618 emit(out, "@2 = @1;", smapper.popD(), smapper.pushF());
621 emit(out, "@2 = Math.floor(@1);",
622 smapper.popF(), smapper.pushI());
625 emit(out, "@2 = Math.floor(@1);",
626 smapper.popF(), smapper.pushL());
629 emit(out, "@2 = Math.floor(@1);",
630 smapper.popD(), smapper.pushI());
633 emit(out, "@2 = Math.floor(@1);",
634 smapper.popD(), smapper.pushL());
639 out.append("{ /* number conversion */ }");
641 case opc_aconst_null:
642 emit(out, "@1 = null;", smapper.pushA());
645 emit(out, "@1 = -1;", smapper.pushI());
648 emit(out, "@1 = 0;", smapper.pushI());
651 emit(out, "@1 = 0;", smapper.pushD());
654 emit(out, "@1 = 0;", smapper.pushL());
657 emit(out, "@1 = 0;", smapper.pushF());
660 emit(out, "@1 = 1;", smapper.pushI());
663 emit(out, "@1 = 1;", smapper.pushL());
666 emit(out, "@1 = 1;", smapper.pushF());
669 emit(out, "@1 = 1;", smapper.pushD());
672 emit(out, "@1 = 2;", smapper.pushI());
675 emit(out, "@1 = 2;", smapper.pushF());
678 emit(out, "@1 = 3;", smapper.pushI());
681 emit(out, "@1 = 4;", smapper.pushI());
684 emit(out, "@1 = 5;", smapper.pushI());
687 int indx = readByte(byteCodes, ++i);
688 String v = encodeConstant(indx);
689 int type = VarType.fromConstantType(jc.getTag(indx));
690 emit(out, "@1 = @2;", smapper.pushT(type), v);
695 int indx = readIntArg(byteCodes, i);
697 String v = encodeConstant(indx);
698 int type = VarType.fromConstantType(jc.getTag(indx));
699 emit(out, "@1 = @2;", smapper.pushT(type), v);
703 emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
704 smapper.popL(), smapper.popL(), smapper.pushI());
708 emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
709 smapper.popF(), smapper.popF(), smapper.pushI());
713 emit(out, "@3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);",
714 smapper.popD(), smapper.popD(), smapper.pushI());
717 i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
721 i = generateIf(byteCodes, i, smapper.popA(), smapper.popA(),
725 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
729 int indx = i + readIntArg(byteCodes, i);
730 emit(out, "if (@1 == 0) { gt = @2; continue; }",
731 smapper.popI(), Integer.toString(indx));
736 int indx = i + readIntArg(byteCodes, i);
737 emit(out, "if (@1 != 0) { gt = @2; continue; }",
738 smapper.popI(), Integer.toString(indx));
743 int indx = i + readIntArg(byteCodes, i);
744 emit(out, "if (@1 < 0) { gt = @2; continue; }",
745 smapper.popI(), Integer.toString(indx));
750 int indx = i + readIntArg(byteCodes, i);
751 emit(out, "if (@1 <= 0) { gt = @2; continue; }",
752 smapper.popI(), Integer.toString(indx));
757 int indx = i + readIntArg(byteCodes, i);
758 emit(out, "if (@1 > 0) { gt = @2; continue; }",
759 smapper.popI(), Integer.toString(indx));
764 int indx = i + readIntArg(byteCodes, i);
765 emit(out, "if (@1 >= 0) { gt = @2; continue; }",
766 smapper.popI(), Integer.toString(indx));
770 case opc_ifnonnull: {
771 int indx = i + readIntArg(byteCodes, i);
772 emit(out, "if (@1 !== null) { gt = @2; continue; }",
773 smapper.popA(), Integer.toString(indx));
778 int indx = i + readIntArg(byteCodes, i);
779 emit(out, "if (@1 === null) { gt = @2; continue; }",
780 smapper.popA(), Integer.toString(indx));
785 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
789 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
793 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
797 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
801 i = generateIf(byteCodes, i, smapper.popI(), smapper.popI(),
805 int indx = i + readIntArg(byteCodes, i);
806 emit(out, "gt = @1; continue;", Integer.toString(indx));
810 case opc_lookupswitch: {
811 int table = i / 4 * 4 + 4;
812 int dflt = i + readInt4(byteCodes, table);
814 int n = readInt4(byteCodes, table);
816 out.append("switch (").append(smapper.popI()).append(") {\n");
818 int cnstnt = readInt4(byteCodes, table);
820 int offset = i + readInt4(byteCodes, table);
822 out.append(" case " + cnstnt).append(": gt = " + offset).append("; continue;\n");
824 out.append(" default: gt = " + dflt).append("; continue;\n}");
828 case opc_tableswitch: {
829 int table = i / 4 * 4 + 4;
830 int dflt = i + readInt4(byteCodes, table);
832 int low = readInt4(byteCodes, table);
834 int high = readInt4(byteCodes, table);
836 out.append("switch (").append(smapper.popI()).append(") {\n");
837 while (low <= high) {
838 int offset = i + readInt4(byteCodes, table);
840 out.append(" case " + low).append(": gt = " + offset).append("; continue;\n");
843 out.append(" default: gt = " + dflt).append("; continue;\n}");
847 case opc_invokeinterface: {
848 i = invokeVirtualMethod(byteCodes, i, smapper) + 2;
851 case opc_invokevirtual:
852 i = invokeVirtualMethod(byteCodes, i, smapper);
854 case opc_invokespecial:
855 i = invokeStaticMethod(byteCodes, i, smapper, false);
857 case opc_invokestatic:
858 i = invokeStaticMethod(byteCodes, i, smapper, true);
861 int indx = readIntArg(byteCodes, i);
862 String ci = jc.getClassName(indx);
863 emit(out, "@1 = new @2;",
864 smapper.pushA(), accessClass(ci.replace('/', '_')));
870 ++i; // skip type of array
871 emit(out, "@2 = new Array(@1).fillNulls();",
872 smapper.popI(), smapper.pushA());
875 i += 2; // skip type of array
876 emit(out, "@2 = new Array(@1).fillNulls();",
877 smapper.popI(), smapper.pushA());
879 case opc_multianewarray: {
881 int dim = readByte(byteCodes, ++i);
882 out.append("{ var a0 = new Array(").append(smapper.popI())
883 .append(").fillNulls();");
884 for (int d = 1; d < dim; d++) {
885 out.append("\n var l" + d).append(" = ")
886 .append(smapper.popI()).append(';');
887 out.append("\n for (var i" + d).append (" = 0; i" + d).
888 append(" < a" + (d - 1)).
889 append(".length; i" + d).append("++) {");
890 out.append("\n var a" + d).
891 append (" = new Array(l" + d).append(").fillNulls();");
892 out.append("\n a" + (d - 1)).append("[i" + d).append("] = a" + d).
895 for (int d = 1; d < dim; d++) {
898 out.append("\n").append(smapper.pushA()).append(" = a0; }");
901 case opc_arraylength:
902 emit(out, "@2 = @1.length;", smapper.popA(), smapper.pushI());
905 emit(out, "@3[@2] = @1;",
906 smapper.popL(), smapper.popI(), smapper.popA());
909 emit(out, "@3[@2] = @1;",
910 smapper.popF(), smapper.popI(), smapper.popA());
913 emit(out, "@3[@2] = @1;",
914 smapper.popD(), smapper.popI(), smapper.popA());
917 emit(out, "@3[@2] = @1;",
918 smapper.popA(), smapper.popI(), smapper.popA());
924 emit(out, "@3[@2] = @1;",
925 smapper.popI(), smapper.popI(), smapper.popA());
928 emit(out, "@3 = @2[@1];",
929 smapper.popI(), smapper.popA(), smapper.pushL());
932 emit(out, "@3 = @2[@1];",
933 smapper.popI(), smapper.popA(), smapper.pushF());
936 emit(out, "@3 = @2[@1];",
937 smapper.popI(), smapper.popA(), smapper.pushD());
940 emit(out, "@3 = @2[@1];",
941 smapper.popI(), smapper.popA(), smapper.pushA());
947 emit(out, "@3 = @2[@1];",
948 smapper.popI(), smapper.popA(), smapper.pushI());
953 out.append("/* pop */");
956 final Variable v = smapper.get(0);
957 emit(out, "@1 = @2;", smapper.pushT(v.getType()), v);
961 if (smapper.get(0).isCategory2()) {
962 final Variable v = smapper.get(0);
963 emit(out, "@1 = @2;", smapper.pushT(v.getType()), v);
965 final Variable v1 = smapper.get(0);
966 final Variable v2 = smapper.get(1);
967 emit(out, "{ @1 = @2; @3 = @4; }",
968 smapper.pushT(v2.getType()), v2,
969 smapper.pushT(v1.getType()), v1);
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);
985 if (smapper.get(1).isCategory2()) {
986 final Variable vi1 = smapper.pop();
987 final Variable vi2 = smapper.pop();
988 final Variable vo3 = smapper.pushT(vi1.getType());
989 final Variable vo2 = smapper.pushT(vi2.getType());
990 final Variable vo1 = smapper.pushT(vi1.getType());
992 emit(out, "{ @1 = @2; @3 = @4; @5 = @6; }",
993 vo1, vi1, vo2, vi2, vo3, vo1);
995 final Variable vi1 = smapper.pop();
996 final Variable vi2 = smapper.pop();
997 final Variable vi3 = smapper.pop();
998 final Variable vo4 = smapper.pushT(vi1.getType());
999 final Variable vo3 = smapper.pushT(vi3.getType());
1000 final Variable vo2 = smapper.pushT(vi2.getType());
1001 final Variable vo1 = smapper.pushT(vi1.getType());
1003 emit(out, "{ @1 = @2; @3 = @4; @5 = @6; @7 = @8; }",
1004 vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1);
1009 emit(out, "@1 = @2;",
1010 smapper.pushI(), Integer.toString(byteCodes[++i]));
1013 emit(out, "@1 = @2;",
1015 Integer.toString(readIntArg(byteCodes, i)));
1018 case opc_getfield: {
1019 int indx = readIntArg(byteCodes, i);
1020 String[] fi = jc.getFieldInfoName(indx);
1021 final int type = VarType.fromFieldType(fi[2].charAt(0));
1022 emit(out, "@2 = @1.fld_@3;",
1023 smapper.popA(), smapper.pushT(type), fi[1]);
1027 case opc_getstatic: {
1028 int indx = readIntArg(byteCodes, i);
1029 String[] fi = jc.getFieldInfoName(indx);
1030 final int type = VarType.fromFieldType(fi[2].charAt(0));
1031 emit(out, "@1 = @2.@3;",
1032 smapper.pushT(type),
1033 accessClass(fi[0].replace('/', '_')), fi[1]);
1035 addReference(fi[0]);
1038 case opc_putfield: {
1039 int indx = readIntArg(byteCodes, i);
1040 String[] fi = jc.getFieldInfoName(indx);
1041 final int type = VarType.fromFieldType(fi[2].charAt(0));
1042 emit(out, "@2.fld_@3 = @1;",
1043 smapper.popT(type), smapper.popA(), fi[1]);
1047 case opc_putstatic: {
1048 int indx = readIntArg(byteCodes, i);
1049 String[] fi = jc.getFieldInfoName(indx);
1050 final int type = VarType.fromFieldType(fi[2].charAt(0));
1051 emit(out, "@1.@2 = @3;",
1052 accessClass(fi[0].replace('/', '_')), fi[1],
1053 smapper.popT(type));
1055 addReference(fi[0]);
1058 case opc_checkcast: {
1059 int indx = readIntArg(byteCodes, i);
1060 final String type = jc.getClassName(indx);
1061 if (!type.startsWith("[")) {
1062 // no way to check arrays right now
1063 // XXX proper exception
1065 "if (@1 !== null && !@1.$instOf_@2) throw {};",
1066 smapper.getA(0), type.replace('/', '_'));
1071 case opc_instanceof: {
1072 int indx = readIntArg(byteCodes, i);
1073 final String type = jc.getClassName(indx);
1074 emit(out, "@2 = @1.$instOf_@3 ? 1 : 0;",
1075 smapper.popA(), smapper.pushI(),
1076 type.replace('/', '_'));
1081 final Variable v = smapper.popA();
1084 emit(out, "{ @1 = @2; throw @2; }",
1085 smapper.pushA(), v);
1089 case opc_monitorenter: {
1090 out.append("/* monitor enter */");
1095 case opc_monitorexit: {
1096 out.append("/* monitor exit */");
1102 emit(out, "throw 'unknown bytecode @1';",
1103 Integer.toString(c));
1107 for (int j = prev; j <= i; j++) {
1109 final int cc = readByte(byteCodes, j);
1110 out.append(Integer.toString(cc));
1118 private int generateIf(byte[] byteCodes, int i,
1119 final Variable v2, final Variable v1,
1120 final String test) throws IOException {
1121 int indx = i + readIntArg(byteCodes, i);
1122 out.append("if (").append(v1)
1123 .append(' ').append(test).append(' ')
1124 .append(v2).append(") { gt = " + indx)
1125 .append("; continue; }");
1129 private int readIntArg(byte[] byteCodes, int offsetInstruction) {
1130 final int indxHi = byteCodes[offsetInstruction + 1] << 8;
1131 final int indxLo = byteCodes[offsetInstruction + 2];
1132 return (indxHi & 0xffffff00) | (indxLo & 0xff);
1134 private int readInt4(byte[] byteCodes, int offsetInstruction) {
1135 final int d = byteCodes[offsetInstruction + 0] << 24;
1136 final int c = byteCodes[offsetInstruction + 1] << 16;
1137 final int b = byteCodes[offsetInstruction + 2] << 8;
1138 final int a = byteCodes[offsetInstruction + 3];
1139 return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff);
1141 private int readByte(byte[] byteCodes, int offsetInstruction) {
1142 return byteCodes[offsetInstruction] & 0xff;
1145 private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) {
1147 Boolean count = null;
1148 boolean array = false;
1150 int firstPos = sig.length();
1151 while (i < descriptor.length()) {
1152 char ch = descriptor.charAt(i++);
1173 if (ch == 'J' || ch == 'D') {
1179 sig.insert(firstPos, ch);
1181 returnType[0] = '[';
1182 sig.insert(firstPos, "_3");
1191 returnType[0] = 'V';
1192 sig.insert(firstPos, 'V');
1195 int next = descriptor.indexOf(';', i);
1196 String realSig = mangleSig(descriptor, i - 1, next + 1);
1201 sig.append(realSig);
1204 sig.insert(firstPos, realSig);
1206 sig.insert(firstPos, "_3");
1208 returnType[0] = 'L';
1216 throw new IllegalStateException("Invalid char: " + ch);
1221 private static String mangleSig(String txt, int first, int last) {
1222 StringBuilder sb = new StringBuilder();
1223 for (int i = first; i < last; i++) {
1224 final char ch = txt.charAt(i);
1226 case '/': sb.append('_'); break;
1227 case '_': sb.append("_1"); break;
1228 case ';': sb.append("_2"); break;
1229 case '[': sb.append("_3"); break;
1230 default: sb.append(ch); break;
1233 return sb.toString();
1236 private static String findMethodName(MethodData m, StringBuilder cnt) {
1237 StringBuilder name = new StringBuilder();
1238 if ("<init>".equals(m.getName())) { // NOI18N
1239 name.append("cons"); // NOI18N
1240 } else if ("<clinit>".equals(m.getName())) { // NOI18N
1241 name.append("class"); // NOI18N
1243 name.append(m.getName());
1246 countArgs(m.getInternalSig(), new char[1], name, cnt);
1247 return name.toString();
1250 static String findMethodName(String[] mi, StringBuilder cnt, char[] returnType) {
1251 StringBuilder name = new StringBuilder();
1252 String descr = mi[2];//mi.getDescriptor();
1254 if ("<init>".equals(nm)) { // NOI18N
1255 name.append("cons"); // NOI18N
1259 countArgs(descr, returnType, name, cnt);
1260 return name.toString();
1263 private int invokeStaticMethod(byte[] byteCodes, int i, final StackMapper mapper, boolean isStatic)
1264 throws IOException {
1265 int methodIndex = readIntArg(byteCodes, i);
1266 String[] mi = jc.getFieldInfoName(methodIndex);
1267 char[] returnType = { 'V' };
1268 StringBuilder cnt = new StringBuilder();
1269 String mn = findMethodName(mi, cnt, returnType);
1271 final int numArguments = isStatic ? cnt.length() : cnt.length() + 1;
1272 final Variable[] vars = new Variable[numArguments];
1274 for (int j = numArguments - 1; j >= 0; --j) {
1275 vars[j] = mapper.pop();
1278 if (returnType[0] != 'V') {
1279 out.append(mapper.pushT(VarType.fromFieldType(returnType[0])))
1283 final String in = mi[0];
1284 out.append(accessClass(in.replace('/', '_')));
1285 out.append("(false).");
1288 if (numArguments > 0) {
1289 out.append(vars[0]);
1290 for (int j = 1; j < numArguments; ++j) {
1292 out.append(vars[j]);
1300 private int invokeVirtualMethod(byte[] byteCodes, int i, final StackMapper mapper)
1301 throws IOException {
1302 int methodIndex = readIntArg(byteCodes, i);
1303 String[] mi = jc.getFieldInfoName(methodIndex);
1304 char[] returnType = { 'V' };
1305 StringBuilder cnt = new StringBuilder();
1306 String mn = findMethodName(mi, cnt, returnType);
1308 final int numArguments = cnt.length() + 1;
1309 final Variable[] vars = new Variable[numArguments];
1311 for (int j = numArguments - 1; j >= 0; --j) {
1312 vars[j] = mapper.pop();
1315 if (returnType[0] != 'V') {
1316 out.append(mapper.pushT(VarType.fromFieldType(returnType[0])))
1320 out.append(vars[0]).append('.');
1323 out.append(vars[0]);
1324 for (int j = 1; j < numArguments; ++j) {
1326 out.append(vars[j]);
1333 private void addReference(String cn) throws IOException {
1334 if (requireReference(cn)) {
1335 out.append(" /* needs ").append(cn).append(" */");
1339 private void outType(String d, StringBuilder out) {
1341 while (d.charAt(0) == '[') {
1345 if (d.charAt(0) == 'L') {
1346 assert d.charAt(d.length() - 1) == ';';
1347 out.append(d.replace('/', '_').substring(0, d.length() - 1));
1353 private String encodeConstant(int entryIndex) throws IOException {
1354 String[] classRef = { null };
1355 String s = jc.stringValue(entryIndex, classRef);
1356 if (classRef[0] != null) {
1357 addReference(classRef[0]);
1358 s = accessClass(s.replace('/', '_')) + "(false).constructor.$class";
1363 private String javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException {
1364 byte[] arr = m.findAnnotationData(true);
1368 final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;";
1369 class P extends AnnotationParser {
1375 String[] args = new String[30];
1379 protected void visitAttr(String type, String attr, String at, String value) {
1380 if (type.equals(jvmType)) {
1381 if ("body".equals(attr)) {
1383 } else if ("args".equals(attr)) {
1384 args[cnt++] = value;
1386 throw new IllegalArgumentException(attr);
1393 if (p.body == null) {
1396 StringBuilder cnt = new StringBuilder();
1397 final String mn = findMethodName(m, cnt);
1398 out.append(prefix).append(mn);
1399 out.append(" = function(");
1403 space = outputArg(out, p.args, 0);
1409 for (int i = 0; i < cnt.length(); i++) {
1411 space = outputArg(out, p.args, index);
1414 out.append(") {").append("\n");
1416 out.append("\n}\n");
1419 private static String className(ClassData jc) {
1420 //return jc.getName().getInternalName().replace('/', '_');
1421 return jc.getClassName().replace('/', '_');
1424 private static String[] findAnnotation(
1425 byte[] arr, ClassData cd, final String className,
1426 final String... attrNames
1427 ) throws IOException {
1431 final String[] values = new String[attrNames.length];
1432 final boolean[] found = { false };
1433 final String jvmType = "L" + className.replace('.', '/') + ";";
1434 AnnotationParser ap = new AnnotationParser(false) {
1436 protected void visitAttr(String type, String attr, String at, String value) {
1437 if (type.equals(jvmType)) {
1439 for (int i = 0; i < attrNames.length; i++) {
1440 if (attrNames[i].equals(attr)) {
1449 return found[0] ? values : null;
1452 private CharSequence initField(FieldData v) {
1453 final String is = v.getInternalSig();
1454 if (is.length() == 1) {
1455 switch (is.charAt(0)) {
1461 case 'I': return " = 0;";
1463 case 'D': return " = 0.0;";
1465 throw new IllegalStateException(is);
1471 private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException {
1472 AnnotationParser ap = new AnnotationParser(true) {
1477 protected void visitAnnotationStart(String type) throws IOException {
1481 out.append('"').append(type).append("\" : {\n");
1486 protected void visitAnnotationEnd(String type) throws IOException {
1487 out.append("\n}\n");
1491 protected void visitAttr(String type, String attr, String attrType, String value)
1492 throws IOException {
1499 out.append(attr).append("__").append(attrType).append(" : ").append(value);
1505 private static String outputArg(Appendable out, String[] args, int indx) throws IOException {
1506 final String name = args[indx];
1510 if (name.contains(",")) {
1511 throw new IOException("Wrong parameter with ',': " + name);
1517 private static void emit(final Appendable out,
1518 final String format,
1519 final CharSequence... params) throws IOException {
1520 final int length = format.length();
1523 int paramOffset = format.indexOf('@');
1524 while ((paramOffset != -1) && (paramOffset < (length - 1))) {
1525 final char paramChar = format.charAt(paramOffset + 1);
1526 if ((paramChar >= '1') && (paramChar <= '9')) {
1527 final int paramIndex = paramChar - '0' - 1;
1529 out.append(format, processed, paramOffset);
1530 out.append(params[paramIndex]);
1533 processed = paramOffset + 1;
1536 paramOffset = format.indexOf('@', paramOffset + 1);
1539 out.append(format, processed, length);