1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue Feb 26 16:54:16 2013 +0100
1.3 @@ -0,0 +1,230 @@
1.4 +/**
1.5 + * Back 2 Browser Bytecode Translator
1.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, version 2 of the License.
1.11 + *
1.12 + * This program is distributed in the hope that it will be useful,
1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.15 + * GNU General Public License for more details.
1.16 + *
1.17 + * You should have received a copy of the GNU General Public License
1.18 + * along with this program. Look for COPYING file in the top folder.
1.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
1.20 + */
1.21 +package org.apidesign.vm4brwsr;
1.22 +
1.23 +import java.io.IOException;
1.24 +import java.io.InputStream;
1.25 +
1.26 +/** Generator of JavaScript from bytecode of classes on classpath of the VM.
1.27 + *
1.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
1.29 + */
1.30 +class VM extends ByteCodeToJavaScript {
1.31 + public VM(Appendable out) {
1.32 + super(out);
1.33 + }
1.34 +
1.35 + static {
1.36 + // uses VMLazy to load dynamic classes
1.37 + boolean assertsOn = false;
1.38 + assert assertsOn = true;
1.39 + if (assertsOn) {
1.40 + VMLazy.init();
1.41 + Zips.init();
1.42 + }
1.43 + }
1.44 +
1.45 + @Override
1.46 + boolean debug(String msg) throws IOException {
1.47 + return false;
1.48 + }
1.49 +
1.50 + static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException {
1.51 + new VM(out).doCompile(l, names);
1.52 + }
1.53 + protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException {
1.54 + out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {");
1.55 + StringArray processed = new StringArray();
1.56 + StringArray initCode = new StringArray();
1.57 + for (String baseClass : names.toArray()) {
1.58 + references.add(baseClass);
1.59 + for (;;) {
1.60 + String name = null;
1.61 + for (String n : references.toArray()) {
1.62 + if (processed.contains(n)) {
1.63 + continue;
1.64 + }
1.65 + name = n;
1.66 + }
1.67 + if (name == null) {
1.68 + break;
1.69 + }
1.70 + InputStream is = loadClass(l, name);
1.71 + if (is == null) {
1.72 + throw new IOException("Can't find class " + name);
1.73 + }
1.74 + try {
1.75 + String ic = compile(is);
1.76 + processed.add(name);
1.77 + initCode.add(ic == null ? "" : ic);
1.78 + } catch (RuntimeException ex) {
1.79 + if (out instanceof CharSequence) {
1.80 + CharSequence seq = (CharSequence)out;
1.81 + int lastBlock = seq.length();
1.82 + while (lastBlock-- > 0) {
1.83 + if (seq.charAt(lastBlock) == '{') {
1.84 + break;
1.85 + }
1.86 + }
1.87 + throw new IOException("Error while compiling " + name + "\n"
1.88 + + seq.subSequence(lastBlock + 1, seq.length()), ex
1.89 + );
1.90 + } else {
1.91 + throw new IOException("Error while compiling " + name + "\n"
1.92 + + out, ex
1.93 + );
1.94 + }
1.95 + }
1.96 + }
1.97 +
1.98 + for (String resource : scripts.toArray()) {
1.99 + while (resource.startsWith("/")) {
1.100 + resource = resource.substring(1);
1.101 + }
1.102 + InputStream emul = l.get(resource);
1.103 + if (emul == null) {
1.104 + throw new IOException("Can't find " + resource);
1.105 + }
1.106 + readResource(emul, out);
1.107 + }
1.108 + scripts = new StringArray();
1.109 +
1.110 + StringArray toInit = StringArray.asList(references.toArray());
1.111 + toInit.reverse();
1.112 +
1.113 + for (String ic : toInit.toArray()) {
1.114 + int indx = processed.indexOf(ic);
1.115 + if (indx >= 0) {
1.116 + out.append(initCode.toArray()[indx]).append("\n");
1.117 + initCode.toArray()[indx] = "";
1.118 + }
1.119 + }
1.120 + }
1.121 + out.append(
1.122 + " return vm;\n"
1.123 + + " };\n"
1.124 + + " global.bck2brwsr = function() {\n"
1.125 + + " var args = Array.prototype.slice.apply(arguments);\n"
1.126 + + " var vm = fillInVMSkeleton({});\n"
1.127 + + " var loader = {};\n"
1.128 + + " loader.vm = vm;\n"
1.129 + + " loader.loadClass = function(name) {\n"
1.130 + + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n"
1.131 + + " var fn = vm[attr];\n"
1.132 + + " if (fn) return fn(false);\n"
1.133 + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
1.134 + + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n"
1.135 + + " }\n"
1.136 + + " if (vm.loadClass) {\n"
1.137 + + " throw 'Cannot initialize the bck2brwsr VM twice!';\n"
1.138 + + " }\n"
1.139 + + " vm.loadClass = loader.loadClass;\n"
1.140 + + " vm.loadBytes = function(name) {\n"
1.141 + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
1.142 + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n"
1.143 + + " }\n"
1.144 + + " vm.java_lang_reflect_Array(false);\n"
1.145 + + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n"
1.146 + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, null, args);\n"
1.147 + + " return loader;\n"
1.148 + + " };\n");
1.149 + out.append("}(this));");
1.150 + }
1.151 + private static void readResource(InputStream emul, Appendable out) throws IOException {
1.152 + try {
1.153 + int state = 0;
1.154 + for (;;) {
1.155 + int ch = emul.read();
1.156 + if (ch == -1) {
1.157 + break;
1.158 + }
1.159 + if (ch < 0 || ch > 255) {
1.160 + throw new IOException("Invalid char in emulation " + ch);
1.161 + }
1.162 + switch (state) {
1.163 + case 0:
1.164 + if (ch == '/') {
1.165 + state = 1;
1.166 + } else {
1.167 + out.append((char)ch);
1.168 + }
1.169 + break;
1.170 + case 1:
1.171 + if (ch == '*') {
1.172 + state = 2;
1.173 + } else {
1.174 + out.append('/').append((char)ch);
1.175 + state = 0;
1.176 + }
1.177 + break;
1.178 + case 2:
1.179 + if (ch == '*') {
1.180 + state = 3;
1.181 + }
1.182 + break;
1.183 + case 3:
1.184 + if (ch == '/') {
1.185 + state = 0;
1.186 + } else {
1.187 + state = 2;
1.188 + }
1.189 + break;
1.190 + }
1.191 + }
1.192 + } finally {
1.193 + emul.close();
1.194 + }
1.195 + }
1.196 +
1.197 + private static InputStream loadClass(Bck2Brwsr.Resources l, String name) throws IOException {
1.198 + return l.get(name + ".class"); // NOI18N
1.199 + }
1.200 +
1.201 + static String toString(String name) throws IOException {
1.202 + StringBuilder sb = new StringBuilder();
1.203 +// compile(sb, name);
1.204 + return sb.toString().toString();
1.205 + }
1.206 +
1.207 + private StringArray scripts = new StringArray();
1.208 + private StringArray references = new StringArray();
1.209 +
1.210 + @Override
1.211 + protected boolean requireReference(String cn) {
1.212 + if (references.contains(cn)) {
1.213 + return false;
1.214 + }
1.215 + references.add(cn);
1.216 + return true;
1.217 + }
1.218 +
1.219 + @Override
1.220 + protected void requireScript(String resourcePath) {
1.221 + scripts.add(resourcePath);
1.222 + }
1.223 +
1.224 + @Override
1.225 + String assignClass(String className) {
1.226 + return "vm." + className + " = ";
1.227 + }
1.228 +
1.229 + @Override
1.230 + String accessClass(String className) {
1.231 + return "vm." + className;
1.232 + }
1.233 +}