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 {
35 private final Appendable out;
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 p = CLS.prototype = ").
103 append(sc.replace('/', '_')).append("(true);");
104 out.append("\n var c = p;");
106 out.append("\n var p = CLS.prototype = ").append(proto[1]).append(";");
107 out.append("\n var c = ").append(proto[0]).append(";");
109 for (MethodData m : jc.getMethods()) {
110 byte[] onlyArr = m.findAnnotationData(true);
111 String[] only = findAnnotation(onlyArr, jc,
112 "org.apidesign.bck2brwsr.core.JavaScriptOnly",
116 if (only[0] != null && only[1] != null) {
117 out.append("\n p.").append(only[0]).append(" = ")
118 .append(only[1]).append(";");
123 generateStaticMethod("\n c.", m, toInitilize);
125 generateInstanceMethod("\n c.", m);
128 out.append("\n c.constructor = CLS;");
129 out.append("\n c.$instOf_").append(className).append(" = true;");
130 for (String superInterface : jc.getSuperInterfaces()) {
131 out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;");
134 out.append("\n if (arguments.length === 0) {");
135 out.append("\n if (!(this instanceof CLS)) {");
136 out.append("\n return new CLS();");
138 for (FieldData v : jc.getFields()) {
139 byte[] onlyArr = v.findAnnotationData(true);
140 String[] only = findAnnotation(onlyArr, jc,
141 "org.apidesign.bck2brwsr.core.JavaScriptOnly",
145 if (only[0] != null && only[1] != null) {
146 out.append("\n p.").append(only[0]).append(" = ")
147 .append(only[1]).append(";");
152 out.append("\n this.fld_").
153 append(v.getName()).append(initField(v));
156 out.append("\n return this;");
158 out.append("\n return arguments[0] ? new CLS() : CLS.prototype;");
160 StringBuilder sb = new StringBuilder();
161 for (String init : toInitilize.toArray()) {
162 sb.append("\n").append(init).append("();");
164 return sb.toString();
166 private void generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException {
167 if (javaScriptBody(prefix, m, true)) {
170 StringBuilder argsCnt = new StringBuilder();
171 final String mn = findMethodName(m, argsCnt);
172 out.append(prefix).append(mn).append(" = function");
173 if (mn.equals("classV")) {
174 toInitilize.add(className(jc) + "(false)." + mn);
178 for (int index = 0, i = 0; i < argsCnt.length(); i++) {
180 out.append("arg").append(String.valueOf(index));
182 final String desc = null;// XXX findDescriptor(args.get(i).getDescriptor());
183 if (argsCnt.charAt(i) == '1') {
189 out.append(") {").append("\n");
190 if (m.getCode() != null) {
191 int len = m.getMaxLocals();
192 for (int i = argsCnt.length(); i < len; i++) {
194 out.append("arg").append(String.valueOf(i)).append(";\n");
198 out.append(" throw 'no code found for ").append(m.getInternalSig()).append("';\n");
203 private void generateInstanceMethod(String prefix, MethodData m) throws IOException {
204 if (javaScriptBody(prefix, m, false)) {
207 StringBuilder argsCnt = new StringBuilder();
208 final String mn = findMethodName(m, argsCnt);
209 out.append(prefix).append(mn).append(" = function");
212 for (int index = 1, i = 0; i < argsCnt.length(); i++) {
214 out.append("arg").append(String.valueOf(index));
215 if (argsCnt.charAt(i) == '1') {
221 out.append(") {").append("\n");
222 if (m.getCode() != null) {
223 int len = m.getMaxLocals();
224 for (int i = argsCnt.length(); i < len; i++) {
226 out.append("arg").append(String.valueOf(i + 1)).append(";\n");
230 out.append(" throw 'no code found for ").append(m.getInternalSig()).append("';\n");
235 private void produceCode(MethodData m) throws IOException {
236 final byte[] byteCodes = m.getCode();
237 final StackMapIterator stackMapIterator = m.createStackMapIterator();
238 final StackToVariableMapper mapper = new StackToVariableMapper();
240 // maxStack includes two stack positions for every pushed long / double
241 // so this might generate more stack variables than we need
242 final int maxStack = m.getMaxStack();
244 out.append("\n var ").append(mapper.constructVariableName(0));
245 for (int i = 1; i < maxStack; ++i) {
247 out.append(mapper.constructVariableName(i));
252 int lastStackFrame = -1;
254 out.append("\n var gt = 0;\n for(;;) switch(gt) {\n");
255 for (int i = 0; i < byteCodes.length; i++) {
257 stackMapIterator.advanceTo(i);
258 if (lastStackFrame != stackMapIterator.getFrameIndex()) {
259 lastStackFrame = stackMapIterator.getFrameIndex();
260 mapper.reset(stackMapIterator.getFrameStackItemsCount());
261 out.append(" case " + i).append(": ");
263 out.append(" /* " + i).append(" */ ");
265 final int c = readByte(byteCodes, i);
272 out.append(mapper.push()).append(" = arg0;");
279 out.append(mapper.push()).append(" = arg1;");
286 out.append(mapper.push()).append(" = arg2;");
293 out.append(mapper.push()).append(" = arg3;");
300 final int indx = readByte(byteCodes, ++i);
301 out.append(mapper.push())
311 final int indx = readByte(byteCodes, ++i);
312 out.append("arg" + indx)
314 .append(mapper.pop())
323 out.append("arg0 = ").append(mapper.pop()).append(';');
330 out.append("arg1 = ").append(mapper.pop()).append(';');
337 out.append("arg2 = ").append(mapper.pop()).append(';');
344 out.append("arg3 = ").append(mapper.pop()).append(';');
350 out.append(mapper.get(1)).append(" += ")
351 .append(mapper.pop()).append(';');
357 out.append(mapper.get(1)).append(" -= ")
358 .append(mapper.pop()).append(';');
364 out.append(mapper.get(1)).append(" *= ")
365 .append(mapper.pop()).append(';');
369 out.append(mapper.get(1))
370 .append(" = Math.floor(")
371 .append(mapper.get(1))
373 .append(mapper.pop())
378 out.append(mapper.get(1)).append(" /= ")
379 .append(mapper.pop()).append(';');
385 out.append(mapper.get(1)).append(" %= ")
386 .append(mapper.pop()).append(';');
390 out.append(mapper.get(1)).append(" &= ")
391 .append(mapper.pop()).append(';');
395 out.append(mapper.get(1)).append(" |= ")
396 .append(mapper.pop()).append(';');
400 out.append(mapper.get(1)).append(" ^= ")
401 .append(mapper.pop()).append(';');
407 out.append(mapper.get(0)).append(" = -")
408 .append(mapper.get(0)).append(';');
412 out.append(mapper.get(1)).append(" <<= ")
413 .append(mapper.pop()).append(';');
417 out.append(mapper.get(1)).append(" >>= ")
418 .append(mapper.pop()).append(';');
422 out.append(mapper.get(1)).append(" >>>= ")
423 .append(mapper.pop()).append(';');
426 final int varIndx = readByte(byteCodes, ++i);
427 final int incrBy = byteCodes[++i];
429 out.append("arg" + varIndx).append("++;");
431 out.append("arg" + varIndx).append(" += " + incrBy).append(";");
436 out.append("return;");
443 out.append("return ").append(mapper.pop()).append(';');
454 out.append("/* number conversion */");
460 out.append(mapper.get(0))
461 .append(" = Math.floor(")
462 .append(mapper.get(0))
468 out.append("/* number conversion */");
470 case opc_aconst_null:
471 out.append(mapper.push()).append(" = null;");
474 out.append(mapper.push()).append(" = -1;");
480 out.append(mapper.push()).append(" = 0;");
486 out.append(mapper.push()).append(" = 1;");
490 out.append(mapper.push()).append(" = 2;");
493 out.append(mapper.push()).append(" = 3;");
496 out.append(mapper.push()).append(" = 4;");
499 out.append(mapper.push()).append(" = 5;");
502 int indx = readByte(byteCodes, ++i);
503 String v = encodeConstant(indx);
504 out.append(mapper.push())
512 int indx = readIntArg(byteCodes, i);
514 String v = encodeConstant(indx);
515 out.append(mapper.push())
526 out.append(mapper.get(1))
528 .append(mapper.get(1))
530 .append(mapper.get(0))
531 .append(") ? 0 : ((")
532 .append(mapper.get(1))
534 .append(mapper.get(0))
535 .append(") ? -1 : 1);");
541 i = generateIf(byteCodes, i, mapper, "===");
544 i = generateIf(byteCodes, i, mapper, "!=");
546 case opc_if_icmpeq: {
547 i = generateIf(byteCodes, i, mapper, "==");
551 int indx = i + readIntArg(byteCodes, i);
552 out.append("if (").append(mapper.pop())
553 .append(" == 0) { gt = " + indx);
554 out.append("; continue; }");
559 int indx = i + readIntArg(byteCodes, i);
560 out.append("if (").append(mapper.pop())
561 .append(" != 0) { gt = " + indx);
562 out.append("; continue; }");
567 int indx = i + readIntArg(byteCodes, i);
568 out.append("if (").append(mapper.pop())
569 .append(" < 0) { gt = " + indx);
570 out.append("; continue; }");
575 int indx = i + readIntArg(byteCodes, i);
576 out.append("if (").append(mapper.pop())
577 .append(" <= 0) { gt = " + indx);
578 out.append("; continue; }");
583 int indx = i + readIntArg(byteCodes, i);
584 out.append("if (").append(mapper.pop())
585 .append(" > 0) { gt = " + indx);
586 out.append("; continue; }");
591 int indx = i + readIntArg(byteCodes, i);
592 out.append("if (").append(mapper.pop())
593 .append(" >= 0) { gt = " + indx);
594 out.append("; continue; }");
598 case opc_ifnonnull: {
599 int indx = i + readIntArg(byteCodes, i);
600 out.append("if (").append(mapper.pop())
601 .append(" !== null) { gt = " + indx);
602 out.append("; continue; }");
607 int indx = i + readIntArg(byteCodes, i);
608 out.append("if (").append(mapper.pop())
609 .append(" === null) { gt = " + indx);
610 out.append("; continue; }");
615 i = generateIf(byteCodes, i, mapper, "!=");
618 i = generateIf(byteCodes, i, mapper, "<");
621 i = generateIf(byteCodes, i, mapper, "<=");
624 i = generateIf(byteCodes, i, mapper, ">");
627 i = generateIf(byteCodes, i, mapper, ">=");
630 int indx = i + readIntArg(byteCodes, i);
631 out.append("gt = " + indx).append("; continue;");
635 case opc_lookupswitch: {
636 int table = i / 4 * 4 + 4;
637 int dflt = i + readInt4(byteCodes, table);
639 int n = readInt4(byteCodes, table);
641 out.append("switch (").append(mapper.pop()).append(") {\n");
643 int cnstnt = readInt4(byteCodes, table);
645 int offset = i + readInt4(byteCodes, table);
647 out.append(" case " + cnstnt).append(": gt = " + offset).append("; continue;\n");
649 out.append(" default: gt = " + dflt).append("; continue;\n}");
653 case opc_tableswitch: {
654 int table = i / 4 * 4 + 4;
655 int dflt = i + readInt4(byteCodes, table);
657 int low = readInt4(byteCodes, table);
659 int high = readInt4(byteCodes, table);
661 out.append("switch (").append(mapper.pop()).append(") {\n");
662 while (low <= high) {
663 int offset = i + readInt4(byteCodes, table);
665 out.append(" case " + low).append(": gt = " + offset).append("; continue;\n");
668 out.append(" default: gt = " + dflt).append("; continue;\n}");
672 case opc_invokeinterface: {
673 i = invokeVirtualMethod(byteCodes, i, mapper) + 2;
676 case opc_invokevirtual:
677 i = invokeVirtualMethod(byteCodes, i, mapper);
679 case opc_invokespecial:
680 i = invokeStaticMethod(byteCodes, i, mapper, false);
682 case opc_invokestatic:
683 i = invokeStaticMethod(byteCodes, i, mapper, true);
686 int indx = readIntArg(byteCodes, i);
687 String ci = jc.getClassName(indx);
688 out.append(mapper.push()).append(" = ");
689 out.append("new ").append(ci.replace('/','_'));
696 ++i; // skip type of array
697 out.append(mapper.get(0))
698 .append(" = new Array(")
699 .append(mapper.get(0))
700 .append(").fillNulls();");
703 case opc_anewarray: {
704 i += 2; // skip type of array
705 out.append(mapper.get(0))
706 .append(" = new Array(")
707 .append(mapper.get(0))
708 .append(").fillNulls();");
711 case opc_multianewarray: {
713 int dim = readByte(byteCodes, ++i);
714 out.append("{ var a0 = new Array(").append(mapper.pop())
715 .append(").fillNulls();");
716 for (int d = 1; d < dim; d++) {
717 out.append("\n var l" + d).append(" = ")
718 .append(mapper.pop()).append(';');
719 out.append("\n for (var i" + d).append (" = 0; i" + d).
720 append(" < a" + (d - 1)).
721 append(".length; i" + d).append("++) {");
722 out.append("\n var a" + d).
723 append (" = new Array(l" + d).append(").fillNulls();");
724 out.append("\n a" + (d - 1)).append("[i" + d).append("] = a" + d).
727 for (int d = 1; d < dim; d++) {
730 out.append("\n").append(mapper.push()).append(" = a0; }");
733 case opc_arraylength:
734 out.append(mapper.get(0)).append(" = ")
735 .append(mapper.get(0)).append(".length;");
745 out.append(mapper.get(2))
746 .append('[').append(mapper.get(1)).append(']')
748 .append(mapper.get(0))
761 out.append(mapper.get(1))
763 .append(mapper.get(1))
764 .append('[').append(mapper.pop()).append("];");
770 out.append("/* pop */");
773 out.append(mapper.push()).append(" = ")
774 .append(mapper.get(1)).append(';');
778 out.append(mapper.push()).append(" = ")
779 .append(mapper.get(1)).append("; ");
780 out.append(mapper.get(1)).append(" = ")
781 .append(mapper.get(2)).append("; ");
782 out.append(mapper.get(2)).append(" = ")
783 .append(mapper.get(0)).append("; ");
788 out.append(mapper.push()).append(" = ")
789 .append(mapper.get(1)).append("; ");
790 out.append(mapper.get(1)).append(" = ")
791 .append(mapper.get(2)).append("; ");
792 out.append(mapper.get(2)).append(" = ")
793 .append(mapper.get(3)).append("; ");
794 out.append(mapper.get(3)).append(" = ")
795 .append(mapper.get(0)).append("; ");
799 out.append(mapper.push()).append(" = ")
800 .append(Integer.toString(byteCodes[++i])).append(';');
803 out.append(mapper.push()).append(" = ")
804 .append(Integer.toString(readIntArg(byteCodes, i)))
809 int indx = readIntArg(byteCodes, i);
810 String[] fi = jc.getFieldInfoName(indx);
811 out.append(mapper.get(0)).append(" = ")
812 .append(mapper.get(0)).append(".fld_")
813 .append(fi[1]).append(';');
817 case opc_getstatic: {
818 int indx = readIntArg(byteCodes, i);
819 String[] fi = jc.getFieldInfoName(indx);
820 out.append(mapper.push()).append(" = ")
821 .append(fi[0].replace('/', '_'))
822 .append('.').append(fi[1]).append(';');
827 case opc_putstatic: {
828 int indx = readIntArg(byteCodes, i);
829 String[] fi = jc.getFieldInfoName(indx);
830 out.append(fi[0].replace('/', '_'));
831 out.append('.').append(fi[1]).append(" = ")
832 .append(mapper.pop()).append(';');
838 int indx = readIntArg(byteCodes, i);
839 String[] fi = jc.getFieldInfoName(indx);
840 out.append(mapper.get(1)).append(".fld_").append(fi[1])
842 .append(mapper.get(0)).append(';');
847 case opc_checkcast: {
848 int indx = readIntArg(byteCodes, i);
849 final String type = jc.getClassName(indx);
850 if (!type.startsWith("[")) {
851 // no way to check arrays right now
852 out.append("if (").append(mapper.get(0))
853 .append(".$instOf_").append(type.replace('/', '_'))
854 .append(" != 1) throw {};"); // XXX proper exception
859 case opc_instanceof: {
860 int indx = readIntArg(byteCodes, i);
861 final String type = jc.getClassName(indx);
862 out.append(mapper.get(0)).append(" = ")
863 .append(mapper.get(0)).append(".$instOf_")
864 .append(type.replace('/', '_'))
865 .append(" ? 1 : 0;");
871 out.append(mapper.bottom()).append(" = ")
872 .append(mapper.top()).append("; ");
873 out.append("throw ").append(mapper.bottom()).append("; ");
880 case opc_monitorenter: {
881 out.append("/* monitor enter */");
886 case opc_monitorexit: {
887 out.append("/* monitor exit */");
893 out.append("throw 'unknown bytecode " + c + "';");
898 for (int j = prev; j <= i; j++) {
900 final int cc = readByte(byteCodes, j);
901 out.append(Integer.toString(cc));
907 if (mapper.getMaxStackSize() > maxStack) {
908 throw new IllegalStateException("Incorrect stack usage");
912 private int generateIf(byte[] byteCodes, int i, final StackToVariableMapper mapper, final String test) throws IOException {
913 int indx = i + readIntArg(byteCodes, i);
914 out.append("if (").append(mapper.get(1))
915 .append(' ').append(test).append(' ')
916 .append(mapper.get(0)).append(") { gt = " + indx)
917 .append("; continue; }");
922 private int readIntArg(byte[] byteCodes, int offsetInstruction) {
923 final int indxHi = byteCodes[offsetInstruction + 1] << 8;
924 final int indxLo = byteCodes[offsetInstruction + 2];
925 return (indxHi & 0xffffff00) | (indxLo & 0xff);
927 private int readInt4(byte[] byteCodes, int offsetInstruction) {
928 final int d = byteCodes[offsetInstruction + 0] << 24;
929 final int c = byteCodes[offsetInstruction + 1] << 16;
930 final int b = byteCodes[offsetInstruction + 2] << 8;
931 final int a = byteCodes[offsetInstruction + 3];
932 return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff);
934 private int readByte(byte[] byteCodes, int offsetInstruction) {
935 return byteCodes[offsetInstruction] & 0xff;
938 private static void countArgs(String descriptor, boolean[] hasReturnType, StringBuilder sig, StringBuilder cnt) {
940 Boolean count = null;
941 boolean array = false;
942 int firstPos = sig.length();
943 while (i < descriptor.length()) {
944 char ch = descriptor.charAt(i++);
968 if (ch == 'J' || ch == 'D') {
974 hasReturnType[0] = true;
975 sig.insert(firstPos, ch);
977 sig.insert(firstPos, 'A');
984 hasReturnType[0] = false;
985 sig.insert(firstPos, 'V');
988 int next = descriptor.indexOf(';', i);
994 sig.append(descriptor.substring(i, next).replace('/', '_'));
997 sig.insert(firstPos, descriptor.substring(i, next).replace('/', '_'));
998 sig.insert(firstPos, ch);
1000 sig.insert(firstPos, 'A');
1002 hasReturnType[0] = true;
1010 break; // invalid character
1015 private String findMethodName(MethodData m, StringBuilder cnt) {
1016 StringBuilder name = new StringBuilder();
1017 if ("<init>".equals(m.getName())) { // NOI18N
1018 name.append("cons"); // NOI18N
1019 } else if ("<clinit>".equals(m.getName())) { // NOI18N
1020 name.append("class"); // NOI18N
1022 name.append(m.getName());
1025 boolean hasReturn[] = { false };
1026 countArgs(findDescriptor(m.getInternalSig()), hasReturn, name, cnt);
1027 return name.toString();
1030 private String findMethodName(String[] mi, StringBuilder cnt, boolean[] hasReturn) {
1031 StringBuilder name = new StringBuilder();
1032 String descr = mi[2];//mi.getDescriptor();
1034 if ("<init>".equals(nm)) { // NOI18N
1035 name.append("cons"); // NOI18N
1039 countArgs(findDescriptor(descr), hasReturn, name, cnt);
1040 return name.toString();
1043 private int invokeStaticMethod(byte[] byteCodes, int i, final StackToVariableMapper mapper, boolean isStatic)
1044 throws IOException {
1045 int methodIndex = readIntArg(byteCodes, i);
1046 String[] mi = jc.getFieldInfoName(methodIndex);
1047 boolean[] hasReturn = { false };
1048 StringBuilder cnt = new StringBuilder();
1049 String mn = findMethodName(mi, cnt, hasReturn);
1051 final int numArguments = isStatic ? cnt.length() : cnt.length() + 1;
1054 out.append((numArguments > 0) ? mapper.get(numArguments - 1)
1055 : mapper.push()).append(" = ");
1058 final String in = mi[0];
1059 out.append(in.replace('/', '_'));
1060 out.append("(false).");
1063 if (numArguments > 0) {
1064 out.append(mapper.get(numArguments - 1));
1065 for (int j = numArguments - 2; j >= 0; --j) {
1067 out.append(mapper.get(j));
1071 if (numArguments > 0) {
1072 mapper.pop(hasReturn[0] ? numArguments - 1 : numArguments);
1078 private int invokeVirtualMethod(byte[] byteCodes, int i, final StackToVariableMapper mapper)
1079 throws IOException {
1080 int methodIndex = readIntArg(byteCodes, i);
1081 String[] mi = jc.getFieldInfoName(methodIndex);
1082 boolean[] hasReturn = { false };
1083 StringBuilder cnt = new StringBuilder();
1084 String mn = findMethodName(mi, cnt, hasReturn);
1086 final int numArguments = cnt.length();
1089 out.append(mapper.get(numArguments)).append(" = ");
1092 out.append(mapper.get(numArguments)).append('.');
1095 out.append(mapper.get(numArguments));
1096 for (int j = numArguments - 1; j >= 0; --j) {
1098 out.append(mapper.get(j));
1101 mapper.pop(hasReturn[0] ? numArguments : numArguments + 1);
1106 private void addReference(String cn) throws IOException {
1107 if (requireReference(cn)) {
1108 out.append(" /* needs ").append(cn).append(" */");
1112 private void outType(String d, StringBuilder out) {
1114 while (d.charAt(0) == '[') {
1118 if (d.charAt(0) == 'L') {
1119 assert d.charAt(d.length() - 1) == ';';
1120 out.append(d.replace('/', '_').substring(0, d.length() - 1));
1126 private String encodeConstant(int entryIndex) {
1127 String s = jc.stringValue(entryIndex, true);
1131 private String findDescriptor(String d) {
1132 return d.replace('[', 'A');
1135 private boolean javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException {
1136 byte[] arr = m.findAnnotationData(true);
1140 final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;";
1141 class P extends AnnotationParser {
1143 String[] args = new String[30];
1147 protected void visitAttr(String type, String attr, String value) {
1148 if (type.equals(jvmType)) {
1149 if ("body".equals(attr)) {
1151 } else if ("args".equals(attr)) {
1152 args[cnt++] = value;
1154 throw new IllegalArgumentException(attr);
1161 if (p.body == null) {
1164 StringBuilder cnt = new StringBuilder();
1165 out.append(prefix).append(findMethodName(m, cnt));
1166 out.append(" = function(");
1170 out.append(p.args[0]);
1177 for (int i = 0; i < cnt.length(); i++) {
1179 out.append(p.args[index]);
1183 out.append(") {").append("\n");
1185 out.append("\n}\n");
1188 private static String className(ClassData jc) {
1189 //return jc.getName().getInternalName().replace('/', '_');
1190 return jc.getClassName().replace('/', '_');
1193 private static String[] findAnnotation(
1194 byte[] arr, ClassData cd, final String className,
1195 final String... attrNames
1196 ) throws IOException {
1200 final String[] values = new String[attrNames.length];
1201 final boolean[] found = { false };
1202 final String jvmType = "L" + className.replace('.', '/') + ";";
1203 AnnotationParser ap = new AnnotationParser() {
1205 protected void visitAttr(String type, String attr, String value) {
1206 if (type.equals(jvmType)) {
1208 for (int i = 0; i < attrNames.length; i++) {
1209 if (attrNames[i].equals(attr)) {
1218 return found[0] ? values : null;
1221 private CharSequence initField(FieldData v) {
1222 final String is = v.getInternalSig();
1223 if (is.length() == 1) {
1224 switch (is.charAt(0)) {
1230 case 'I': return " = 0;";
1232 case 'D': return " = 0.0;";
1234 throw new IllegalStateException(is);