Can parse JDK8 generated bytecode jdk8
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 26 Jun 2014 23:54:17 +0200
branchjdk8
changeset 16394b09a4b689a4
parent 1638 a84f511654ae
child 1640 f61e9984adff
Can parse JDK8 generated bytecode
rt/pom.xml
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
rt/vm8/pom.xml
rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/BytesLoader.java
rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/Lambdas.java
rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/LambdasTest.java
rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/TestVM.java
     1.1 --- a/rt/pom.xml	Thu Jun 26 07:42:54 2014 +0200
     1.2 +++ b/rt/pom.xml	Thu Jun 26 23:54:17 2014 +0200
     1.3 @@ -19,4 +19,14 @@
     1.4      <module>vmtest</module>
     1.5      <module>aot</module>
     1.6    </modules>
     1.7 +  <profiles>
     1.8 +      <profile>
     1.9 +          <activation>
    1.10 +              <jdk>1.8</jdk>
    1.11 +          </activation>
    1.12 +          <modules>
    1.13 +              <module>vm8</module>
    1.14 +          </modules>
    1.15 +      </profile>
    1.16 +  </profiles>
    1.17  </project>
     2.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java	Thu Jun 26 07:42:54 2014 +0200
     2.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java	Thu Jun 26 23:54:17 2014 +0200
     2.3 @@ -58,6 +58,9 @@
     2.4      public static final int CONSTANT_METHOD              = 10;
     2.5      public static final int CONSTANT_INTERFACEMETHOD     = 11;
     2.6      public static final int CONSTANT_NAMEANDTYPE         = 12;
     2.7 +    public static final int CONSTANT_METHODHANDLE     = 15;
     2.8 +    public static final int CONSTANT_METHODTYPE     = 16;
     2.9 +    public static final int CONSTANT_INVOKEDYNAMIC     = 18;
    2.10  
    2.11      /* Access Flags */
    2.12      public static final int ACC_PUBLIC                   = 0x00000001;
    2.13 @@ -668,7 +671,17 @@
    2.14                      case CONSTANT_NAMEANDTYPE:
    2.15                          cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort());
    2.16                          break;
    2.17 -
    2.18 +                    case CONSTANT_METHODHANDLE:
    2.19 +                        in.readByte();
    2.20 +                        in.readUnsignedShort();
    2.21 +                        break;
    2.22 +                    case CONSTANT_METHODTYPE:
    2.23 +                        in.readUnsignedShort();
    2.24 +                        break;
    2.25 +                    case CONSTANT_INVOKEDYNAMIC:
    2.26 +                        in.readUnsignedShort();
    2.27 +                        in.readUnsignedShort();
    2.28 +                        break;
    2.29                      case 0:
    2.30                      default:
    2.31                          throw new ClassFormatError("invalid constant type: " + (int) tags[i]);
     3.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Thu Jun 26 07:42:54 2014 +0200
     3.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Thu Jun 26 23:54:17 2014 +0200
     3.3 @@ -19,7 +19,6 @@
     3.4  
     3.5  import java.io.IOException;
     3.6  import java.io.InputStream;
     3.7 -import java.util.Locale;
     3.8  import static org.apidesign.vm4brwsr.ByteCodeParser.*;
     3.9  
    3.10  /** Translator of the code inside class files to JavaScript.
    3.11 @@ -1558,7 +1557,7 @@
    3.12                          sb.append(ch);
    3.13                      } else {
    3.14                          sb.append("_0");
    3.15 -                        String hex = Integer.toHexString(ch).toLowerCase(Locale.ENGLISH);
    3.16 +                        String hex = Integer.toHexString(ch).toLowerCase();
    3.17                          for (int m = hex.length(); m < 4; m++) {
    3.18                              sb.append("0");
    3.19                          }
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/rt/vm8/pom.xml	Thu Jun 26 23:54:17 2014 +0200
     4.3 @@ -0,0 +1,39 @@
     4.4 +<?xml version="1.0" encoding="UTF-8"?>
     4.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     4.6 +    <modelVersion>4.0.0</modelVersion>
     4.7 +    <parent>
     4.8 +        <groupId>org.apidesign.bck2brwsr</groupId>
     4.9 +        <artifactId>rt</artifactId>
    4.10 +        <version>1.0-SNAPSHOT</version>
    4.11 +    </parent>
    4.12 +    <artifactId>vm8</artifactId>
    4.13 +    <name>Bck2Brwsr on JDK8</name>
    4.14 +    <packaging>jar</packaging>
    4.15 +    <build>
    4.16 +        <plugins>
    4.17 +            <plugin>
    4.18 +                <groupId>org.apache.maven.plugins</groupId>
    4.19 +                <artifactId>maven-compiler-plugin</artifactId>
    4.20 +                <version>2.3.2</version>
    4.21 +                <configuration>
    4.22 +                    <source>1.8</source>
    4.23 +                    <target>1.8</target>
    4.24 +                </configuration>
    4.25 +            </plugin>
    4.26 +        </plugins>
    4.27 +    </build>
    4.28 +    <dependencies>
    4.29 +        <dependency>
    4.30 +            <groupId>org.testng</groupId>
    4.31 +            <artifactId>testng</artifactId>
    4.32 +            <scope>test</scope>
    4.33 +        </dependency>
    4.34 +        <dependency>
    4.35 +            <groupId>org.apidesign.bck2brwsr</groupId>
    4.36 +            <artifactId>vm4brwsr</artifactId>
    4.37 +            <version>${project.version}</version>
    4.38 +            <scope>test</scope>
    4.39 +            <type>jar</type>
    4.40 +        </dependency>
    4.41 +    </dependencies>
    4.42 +</project>
    4.43 \ No newline at end of file
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/BytesLoader.java	Thu Jun 26 23:54:17 2014 +0200
     5.3 @@ -0,0 +1,69 @@
     5.4 +/**
     5.5 + * Back 2 Browser Bytecode Translator
     5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, version 2 of the License.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details.
    5.16 + *
    5.17 + * You should have received a copy of the GNU General Public License
    5.18 + * along with this program. Look for COPYING file in the top folder.
    5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    5.20 + */
    5.21 +package org.apidesign.bck2brwsr.vm8;
    5.22 +
    5.23 +import java.io.IOException;
    5.24 +import java.io.InputStream;
    5.25 +import java.net.URL;
    5.26 +import java.util.Enumeration;
    5.27 +
    5.28 +/**
    5.29 + *
    5.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    5.31 + */
    5.32 +public final class BytesLoader {
    5.33 +    public byte[] get(String name) throws IOException {
    5.34 +        byte[] arr = readClass(name);
    5.35 +        /*
    5.36 +        System.err.print("loader['" + name + "'] = [");
    5.37 +        for (int i = 0; i < arr.length; i++) {
    5.38 +        if (i > 0) {
    5.39 +        System.err.print(", ");
    5.40 +        }
    5.41 +        System.err.print(arr[i]);
    5.42 +        }
    5.43 +        System.err.println("]");
    5.44 +         */
    5.45 +        return arr;
    5.46 +    }
    5.47 +
    5.48 +    static byte[] readClass(String name) throws IOException {
    5.49 +        URL u = null;
    5.50 +        Enumeration<URL> en = BytesLoader.class.getClassLoader().getResources(name);
    5.51 +        while (en.hasMoreElements()) {
    5.52 +            u = en.nextElement();
    5.53 +        }
    5.54 +        if (u == null) {
    5.55 +            throw new IOException("Can't find " + name);
    5.56 +        }
    5.57 +        try (InputStream is = u.openStream()) {
    5.58 +            byte[] arr;
    5.59 +            arr = new byte[is.available()];
    5.60 +            int offset = 0;
    5.61 +            while (offset < arr.length) {
    5.62 +                int len = is.read(arr, offset, arr.length - offset);
    5.63 +                if (len == -1) {
    5.64 +                    throw new IOException("Can't read " + name);
    5.65 +                }
    5.66 +                offset += len;
    5.67 +            }
    5.68 +            return arr;
    5.69 +        }
    5.70 +    }
    5.71 +    
    5.72 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/Lambdas.java	Thu Jun 26 23:54:17 2014 +0200
     6.3 @@ -0,0 +1,36 @@
     6.4 +/**
     6.5 + * Back 2 Browser Bytecode Translator
     6.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     6.7 + *
     6.8 + * This program is free software: you can redistribute it and/or modify
     6.9 + * it under the terms of the GNU General Public License as published by
    6.10 + * the Free Software Foundation, version 2 of the License.
    6.11 + *
    6.12 + * This program is distributed in the hope that it will be useful,
    6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.15 + * GNU General Public License for more details.
    6.16 + *
    6.17 + * You should have received a copy of the GNU General Public License
    6.18 + * along with this program. Look for COPYING file in the top folder.
    6.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    6.20 + */
    6.21 +package org.apidesign.bck2brwsr.vm8;
    6.22 +
    6.23 +/**
    6.24 + *
    6.25 + * @author Jaroslav Tulach
    6.26 + */
    6.27 +public class Lambdas {
    6.28 +    private static void fewTimes(Runnable r, int cnt) {
    6.29 +        while (cnt-- > 0) {
    6.30 +            r.run();
    6.31 +        }
    6.32 +    }
    6.33 +    
    6.34 +    public static String compound() {
    6.35 +        StringBuilder sb = new StringBuilder();
    6.36 +        fewTimes(() -> sb.append('X'), 10);
    6.37 +        return sb.toString();
    6.38 +    }
    6.39 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/LambdasTest.java	Thu Jun 26 23:54:17 2014 +0200
     7.3 @@ -0,0 +1,53 @@
     7.4 +/**
     7.5 + * Back 2 Browser Bytecode Translator
     7.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     7.7 + *
     7.8 + * This program is free software: you can redistribute it and/or modify
     7.9 + * it under the terms of the GNU General Public License as published by
    7.10 + * the Free Software Foundation, version 2 of the License.
    7.11 + *
    7.12 + * This program is distributed in the hope that it will be useful,
    7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.15 + * GNU General Public License for more details.
    7.16 + *
    7.17 + * You should have received a copy of the GNU General Public License
    7.18 + * along with this program. Look for COPYING file in the top folder.
    7.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    7.20 + */
    7.21 +package org.apidesign.bck2brwsr.vm8;
    7.22 +
    7.23 +import org.testng.annotations.BeforeClass;
    7.24 +import static org.testng.Assert.*;
    7.25 +import org.testng.annotations.AfterClass;
    7.26 +import org.testng.annotations.Test;
    7.27 +
    7.28 +/**
    7.29 + *
    7.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    7.31 + */
    7.32 +public class LambdasTest {
    7.33 +    private static TestVM code;
    7.34 +    
    7.35 +    @Test public void verifyJSTime() throws Exception {
    7.36 +        String exp = Lambdas.compound();
    7.37 +        
    7.38 +        Object js = code.execCode("Get js time", 
    7.39 +            Lambdas.class, "compound__Ljava_lang_String_2",
    7.40 +            exp
    7.41 +        );
    7.42 +    }
    7.43 +    
    7.44 +    
    7.45 +    @BeforeClass 
    7.46 +    public static void compileTheCode() throws Exception {
    7.47 +        code = TestVM.compileClass(
    7.48 +            "org/apidesign/bck2brwsr/vm8/Lambdas");
    7.49 +    }
    7.50 +    @AfterClass
    7.51 +    public static void releaseTheCode() {
    7.52 +        code = null;
    7.53 +    }
    7.54 +    
    7.55 +}
    7.56 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/TestVM.java	Thu Jun 26 23:54:17 2014 +0200
     8.3 @@ -0,0 +1,310 @@
     8.4 +/**
     8.5 + * Back 2 Browser Bytecode Translator
     8.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     8.7 + *
     8.8 + * This program is free software: you can redistribute it and/or modify
     8.9 + * it under the terms of the GNU General Public License as published by
    8.10 + * the Free Software Foundation, version 2 of the License.
    8.11 + *
    8.12 + * This program is distributed in the hope that it will be useful,
    8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.15 + * GNU General Public License for more details.
    8.16 + *
    8.17 + * You should have received a copy of the GNU General Public License
    8.18 + * along with this program. Look for COPYING file in the top folder.
    8.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    8.20 + */
    8.21 +package org.apidesign.bck2brwsr.vm8;
    8.22 +
    8.23 +import java.io.ByteArrayInputStream;
    8.24 +import java.io.File;
    8.25 +import java.io.FileWriter;
    8.26 +import java.io.IOException;
    8.27 +import java.io.InputStream;
    8.28 +import java.net.URL;
    8.29 +import java.util.ArrayList;
    8.30 +import java.util.Arrays;
    8.31 +import java.util.Enumeration;
    8.32 +import java.util.HashSet;
    8.33 +import java.util.List;
    8.34 +import java.util.Set;
    8.35 +import javax.script.Invocable;
    8.36 +import javax.script.ScriptContext;
    8.37 +import javax.script.ScriptEngine;
    8.38 +import javax.script.ScriptEngineManager;
    8.39 +import javax.script.ScriptException;
    8.40 +import org.apidesign.vm4brwsr.Bck2Brwsr;
    8.41 +import org.apidesign.vm4brwsr.ObfuscationLevel;
    8.42 +import static org.testng.Assert.*;
    8.43 +
    8.44 +public final class TestVM {
    8.45 +    private final Invocable code;
    8.46 +    private final CharSequence codeSeq;
    8.47 +    private final Object bck2brwsr;
    8.48 +    private BytesLoader resources;
    8.49 +    
    8.50 +    
    8.51 +    private TestVM(Invocable code, CharSequence codeSeq) throws ScriptException, NoSuchMethodException {
    8.52 +        this.code = code;
    8.53 +        this.codeSeq = codeSeq;
    8.54 +        this.bck2brwsr = ((ScriptEngine)code).eval("bck2brwsr(function(n) { return loader.get(n); })");
    8.55 +        ((ScriptEngine)code).getContext().setAttribute("loader", this, ScriptContext.ENGINE_SCOPE);
    8.56 +    }
    8.57 +    
    8.58 +    public void register(BytesLoader res) {
    8.59 +        this.resources = res;
    8.60 +    }
    8.61 +    
    8.62 +    public byte[] get(String res) throws IOException {
    8.63 +        return resources != null ? resources.get(res) : null;
    8.64 +    }
    8.65 +
    8.66 +    public Object execCode(
    8.67 +        String msg, Class<?> clazz, String method, 
    8.68 +        Object expRes, Object... args
    8.69 +    ) throws Exception {
    8.70 +        Object ret = null;
    8.71 +        try {
    8.72 +            ret = code.invokeMethod(bck2brwsr, "loadClass", clazz.getName());
    8.73 +            List<Object> ma = new ArrayList<>();
    8.74 +            ma.add(method);
    8.75 +            ma.addAll(Arrays.asList(args));
    8.76 +            ret = code.invokeMethod(ret, "invoke", ma.toArray());
    8.77 +        } catch (ScriptException ex) {
    8.78 +            fail("Execution failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
    8.79 +        } catch (NoSuchMethodException ex) {
    8.80 +            fail("Cannot find method in " + dumpJS(codeSeq), ex);
    8.81 +        }
    8.82 +        if (ret == null && expRes == null) {
    8.83 +            return null;
    8.84 +        }
    8.85 +        if (expRes != null && expRes.equals(ret)) {
    8.86 +            return null;
    8.87 +        }
    8.88 +        if (expRes instanceof Number) {
    8.89 +            // in case of Long it is necessary convert it to number
    8.90 +            // since the Long is represented by two numbers in JavaScript
    8.91 +            try {
    8.92 +                final Object toFP = ((ScriptEngine)code).eval("Number.prototype.toFP");
    8.93 +                if (ret instanceof Long) {
    8.94 +                    ret = code.invokeMethod(toFP, "call", ret);
    8.95 +                }
    8.96 +                ret = code.invokeFunction("Number", ret);
    8.97 +            } catch (ScriptException ex) {
    8.98 +                fail("Conversion to number failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
    8.99 +            } catch (NoSuchMethodException ex) {
   8.100 +                fail("Cannot find global Number(x) function in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
   8.101 +            }
   8.102 +        }
   8.103 +        return ret;
   8.104 +    }
   8.105 +    
   8.106 +    void assertExec(
   8.107 +        String msg, Class clazz, String method, Object expRes, Object... args
   8.108 +    ) throws Exception {
   8.109 +        Object ret = execCode(msg, clazz, method, expRes, args);
   8.110 +        if (ret == null) {
   8.111 +            return;
   8.112 +        }
   8.113 +        if (expRes instanceof Integer && ret instanceof Double) {
   8.114 +            expRes = ((Integer)expRes).doubleValue();
   8.115 +        }
   8.116 +        if (expRes != null && expRes.equals(ret)) {
   8.117 +            return;
   8.118 +        }
   8.119 +        assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(codeSeq));
   8.120 +    }    
   8.121 +
   8.122 +    static TestVM compileClass(String... names) throws ScriptException, IOException {
   8.123 +        return compileClass(null, names);
   8.124 +    }
   8.125 +    
   8.126 +    static TestVM compileClass(StringBuilder sb, String... names) throws ScriptException, IOException {
   8.127 +        return compileClass(sb, null, names);
   8.128 +    }
   8.129 +
   8.130 +    static TestVM compileClass(StringBuilder sb, ScriptEngine[] eng, String... names) throws ScriptException, IOException {
   8.131 +        if (sb == null) {
   8.132 +            sb = new StringBuilder();
   8.133 +        }
   8.134 +        Bck2Brwsr.generate(sb, new EmulationResources(), names);
   8.135 +        ScriptEngineManager sem = new ScriptEngineManager();
   8.136 +        ScriptEngine js = sem.getEngineByExtension("js");
   8.137 +        if (eng != null) {
   8.138 +            eng[0] = js;
   8.139 +        }
   8.140 +        try {
   8.141 +            Object res = js.eval(sb.toString());
   8.142 +            assertTrue(js instanceof Invocable, "It is invocable object: " + res);
   8.143 +            return new TestVM((Invocable) js, sb);
   8.144 +        } catch (Exception ex) {
   8.145 +            if (sb.length() > 2000) {
   8.146 +                sb = dumpJS(sb);
   8.147 +            }
   8.148 +            fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
   8.149 +            return null;
   8.150 +        }
   8.151 +    }
   8.152 +    
   8.153 +    static TestVM compileClassAsExtension(
   8.154 +        StringBuilder sb, ScriptEngine[] eng, 
   8.155 +        String name, final String resourceName, final String resourceContent
   8.156 +    ) throws ScriptException, IOException {
   8.157 +        return compileClassesAsExtension(sb, eng, resourceName, resourceContent, name);
   8.158 +    }
   8.159 +    static TestVM compileClassesAsExtension(
   8.160 +        StringBuilder sb, ScriptEngine[] eng, 
   8.161 +        final String resourceName, final String resourceContent, String... names
   8.162 +    ) throws ScriptException, IOException {
   8.163 +        if (sb == null) {
   8.164 +            sb = new StringBuilder();
   8.165 +        }
   8.166 +        if (eng[0] == null) {
   8.167 +            ScriptEngineManager sem = new ScriptEngineManager();
   8.168 +            ScriptEngine js = sem.getEngineByExtension("js");
   8.169 +            eng[0] = js;
   8.170 +            Bck2Brwsr.generate(sb, new EmulationResources());
   8.171 +        }
   8.172 +        Set<String> exp = new HashSet<String>();
   8.173 +        for (String n : names) {
   8.174 +            int last = n.lastIndexOf('/');
   8.175 +            exp.add(n.substring(0, last + 1));
   8.176 +        }
   8.177 +        Bck2Brwsr b2b = Bck2Brwsr.newCompiler().
   8.178 +            resources(new EmulationResources() {
   8.179 +                @Override
   8.180 +                public InputStream get(String name) throws IOException {
   8.181 +                    if (name.equals(resourceName)) {
   8.182 +                        return new ByteArrayInputStream(resourceContent.getBytes("UTF-8"));
   8.183 +                    }
   8.184 +                    return super.get(name);
   8.185 +                }
   8.186 +            }).
   8.187 +            addClasses(names).
   8.188 +            addResources("org/apidesign/vm4brwsr/obj.js").
   8.189 +            addExported(exp.toArray(new String[0])).
   8.190 +            obfuscation(ObfuscationLevel.FULL).
   8.191 +            library();
   8.192 +        if (resourceName != null) {
   8.193 +            b2b = b2b.addResources(resourceName);
   8.194 +        }
   8.195 +        b2b.generate(sb);
   8.196 +        try {
   8.197 +            defineAtoB(eng[0]);
   8.198 +            Object res = eng[0].eval(sb.toString());
   8.199 +            assertTrue(eng[0] instanceof Invocable, "It is invocable object: " + res);
   8.200 +            return new TestVM((Invocable) eng[0], sb);
   8.201 +        } catch (Exception ex) {
   8.202 +            if (sb.length() > 2000) {
   8.203 +                sb = dumpJS(sb);
   8.204 +            }
   8.205 +            fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
   8.206 +            return null;
   8.207 +        }
   8.208 +    }
   8.209 +    
   8.210 +    static TestVM compileClassAndResources(StringBuilder sb, ScriptEngine[] eng, String name, String... resources) throws ScriptException, IOException {
   8.211 +        if (sb == null) {
   8.212 +            sb = new StringBuilder();
   8.213 +        }
   8.214 +        Bck2Brwsr b2b = Bck2Brwsr.newCompiler().
   8.215 +            resources(new EmulationResources()).
   8.216 +            addRootClasses(name).
   8.217 +            addResources(resources);
   8.218 +        b2b.generate(sb);
   8.219 +        ScriptEngineManager sem = new ScriptEngineManager();
   8.220 +        ScriptEngine js = sem.getEngineByExtension("js");
   8.221 +        if (eng != null) {
   8.222 +            eng[0] = js;
   8.223 +        }
   8.224 +        try {
   8.225 +            defineAtoB(js);
   8.226 +            
   8.227 +            Object res = js.eval(sb.toString());
   8.228 +            assertTrue(js instanceof Invocable, "It is invocable object: " + res);
   8.229 +            return new TestVM((Invocable) js, sb);
   8.230 +        } catch (Exception ex) {
   8.231 +            if (sb.length() > 2000) {
   8.232 +                sb = dumpJS(sb);
   8.233 +            }
   8.234 +            fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
   8.235 +            return null;
   8.236 +        }
   8.237 +    }
   8.238 +
   8.239 +    private static void defineAtoB(ScriptEngine js) throws ScriptException {
   8.240 +        js.eval("atob = function(s) { return new String(org.apidesign.vm4brwsr.ResourcesTest.parseBase64Binary(s)); }");
   8.241 +    }
   8.242 +
   8.243 +//    Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException {
   8.244 +//        return code.invokeMethod(bck2brwsr, "loadClass", Exceptions.class.getName());
   8.245 +//    }
   8.246 +    
   8.247 +    Object invokeMethod(Object obj, String method, Object... params) throws ScriptException, NoSuchMethodException {
   8.248 +        return code.invokeMethod(obj, method, params);
   8.249 +    }
   8.250 +
   8.251 +    Object invokeFunction(String methodName, Object... args) throws ScriptException, NoSuchMethodException {
   8.252 +        return code.invokeFunction(methodName, args);
   8.253 +    }
   8.254 +
   8.255 +    static StringBuilder dumpJS(CharSequence sb) throws IOException {
   8.256 +        File f = File.createTempFile("execution", ".js");
   8.257 +        FileWriter w = new FileWriter(f);
   8.258 +        w.append(sb);
   8.259 +        w.close();
   8.260 +        return new StringBuilder(f.getPath());
   8.261 +    }
   8.262 +
   8.263 +    @Override
   8.264 +    public String toString() {
   8.265 +        try {
   8.266 +            return dumpJS(codeSeq).toString();
   8.267 +        } catch (IOException ex) {
   8.268 +            return ex.toString();
   8.269 +        }
   8.270 +    }
   8.271 +
   8.272 +    final CharSequence codeSeq() {
   8.273 +        return codeSeq;
   8.274 +    }
   8.275 +    
   8.276 +    private static class EmulationResources implements Bck2Brwsr.Resources {
   8.277 +        @Override
   8.278 +        public InputStream get(String name) throws IOException {
   8.279 +            if ("java/net/URI.class".equals(name)) {
   8.280 +                // skip
   8.281 +                return null;
   8.282 +            }
   8.283 +            if ("java/net/URLConnection.class".equals(name)) {
   8.284 +                // skip
   8.285 +                return null;
   8.286 +            }
   8.287 +            if ("java/lang/System.class".equals(name)) {
   8.288 +                // skip
   8.289 +                return null;
   8.290 +            }
   8.291 +            if ("java/io/PrintStream.class".equals(name)) {
   8.292 +                // skip
   8.293 +                return null;
   8.294 +            }
   8.295 +            if ("java/io/PrintWriter.class".equals(name)) {
   8.296 +                // skip
   8.297 +                return null;
   8.298 +            }
   8.299 +            Enumeration<URL> en = TestVM.class.getClassLoader().getResources(name);
   8.300 +            URL u = null;
   8.301 +            while (en.hasMoreElements()) {
   8.302 +                u = en.nextElement();
   8.303 +            }
   8.304 +            if (u == null) {
   8.305 +                throw new IOException("Can't find " + name);
   8.306 +            }
   8.307 +            if (u.toExternalForm().contains("rt.jar!")) {
   8.308 +                throw new IOException("No emulation for " + u);
   8.309 +            }
   8.310 +            return u.openStream();
   8.311 +        }
   8.312 +    }
   8.313 +}