rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/TestVM.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 12 Mar 2015 12:07:54 +0100
branchflow
changeset 1815 fbe883b5a793
parent 1812 4fef6b767f61
child 1817 c1fd23f4e0ae
permissions -rw-r--r--
Optimize only simpleLoopTest... method and first check the generated code before compiling it as JavaScript
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     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.
     8  *
     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.
    13  *
    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.
    17  */
    18 package org.apidesign.bck2brwsr.flow;
    19 
    20 import java.io.ByteArrayInputStream;
    21 import java.io.File;
    22 import java.io.FileOutputStream;
    23 import java.io.IOException;
    24 import java.io.InputStream;
    25 import java.io.OutputStreamWriter;
    26 import java.io.Writer;
    27 import java.net.URL;
    28 import java.util.ArrayList;
    29 import java.util.Arrays;
    30 import java.util.Enumeration;
    31 import java.util.HashSet;
    32 import java.util.List;
    33 import java.util.Set;
    34 import javax.script.Invocable;
    35 import javax.script.ScriptContext;
    36 import javax.script.ScriptEngine;
    37 import javax.script.ScriptEngineManager;
    38 import javax.script.ScriptException;
    39 import org.apidesign.vm4brwsr.Bck2Brwsr;
    40 import org.apidesign.vm4brwsr.Bck2Brwsr.Flow;
    41 import org.apidesign.vm4brwsr.ObfuscationLevel;
    42 import static org.testng.Assert.*;
    43 
    44 public final class TestVM {
    45     private final ScriptEngine js;
    46     private Invocable code;
    47     private final CharSequence codeSeq;
    48     private Object bck2brwsr;
    49     
    50     
    51     private TestVM(ScriptEngine js, CharSequence codeSeq) throws ScriptException, NoSuchMethodException {
    52         this.js = js;
    53         this.codeSeq = codeSeq;
    54     }
    55     
    56     public Object execCode(
    57         String msg, Class<?> clazz, String method, 
    58         Object expRes, Object... args
    59     ) throws Exception {
    60         Object ret = null;
    61         try {
    62             ret = getCode().invokeMethod(bck2brwsr, "loadClass", clazz.getName());
    63             List<Object> ma = new ArrayList<Object>();
    64             ma.add(method);
    65             ma.addAll(Arrays.asList(args));
    66             ret = getCode().invokeMethod(ret, "invoke", ma.toArray());
    67         } catch (ScriptException ex) {
    68             fail("Execution failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
    69         } catch (NoSuchMethodException ex) {
    70             fail("Cannot find method in " + dumpJS(codeSeq), ex);
    71         }
    72         if (ret == null && expRes == null) {
    73             return null;
    74         }
    75         if (expRes != null && expRes.equals(ret)) {
    76             return null;
    77         }
    78         if (expRes instanceof Number) {
    79             // in case of Long it is necessary convert it to number
    80             // since the Long is represented by two numbers in JavaScript
    81             try {
    82                 final Object toFP = ((ScriptEngine)getCode()).eval("Number.prototype.toFP");
    83                 if (ret instanceof Long) {
    84                     ret = getCode().invokeMethod(toFP, "call", ret);
    85                 }
    86                 ret = getCode().invokeFunction("Number", ret);
    87             } catch (ScriptException ex) {
    88                 fail("Conversion to number failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
    89             } catch (NoSuchMethodException ex) {
    90                 fail("Cannot find global Number(x) function in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
    91             }
    92         }
    93         return ret;
    94     }
    95     
    96     void assertExec(
    97         String msg, Class clazz, String method, Object expRes, Object... args
    98     ) throws Exception {
    99         Object ret = execCode(msg, clazz, method, expRes, args);
   100         if (ret == null) {
   101             return;
   102         }
   103         if (expRes instanceof Integer && ret instanceof Double) {
   104             expRes = ((Integer)expRes).doubleValue();
   105         }
   106         if (expRes != null && expRes.equals(ret)) {
   107             return;
   108         }
   109         assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(codeSeq));
   110     }    
   111 
   112     static TestVM compileClass(String... names) throws ScriptException, IOException {
   113         return compileClass(null, GraalFlowAnalyzer.getDefault(), names);
   114     }
   115     
   116     static TestVM compileClass(StringBuilder sb, Flow.Analyzer flow, String... names) throws ScriptException, IOException {
   117         return compileClass(sb, null, flow, names);
   118     }
   119 
   120     static TestVM compileClass(StringBuilder sb, 
   121         ScriptEngine[] eng, Flow.Analyzer flow, String... names
   122     ) throws ScriptException, IOException {
   123         return compileClass(sb, eng, flow, new EmulationResources(), names);
   124     }
   125     static TestVM compileClass(
   126         StringBuilder sb, 
   127         ScriptEngine[] eng, 
   128         Flow.Analyzer flow,
   129         Bck2Brwsr.Resources resources, 
   130         String... names
   131     ) throws ScriptException, IOException {
   132         if (sb == null) {
   133             sb = new StringBuilder();
   134         }
   135         Bck2Brwsr.newCompiler()
   136             .resources(resources)
   137             .addClasses(names)
   138             .flowAnalyzer(flow)
   139             .generate(sb);
   140         ScriptEngineManager sem = new ScriptEngineManager();
   141         ScriptEngine js = sem.getEngineByExtension("js");
   142         if (eng != null) {
   143             eng[0] = js;
   144         }
   145         try {
   146             return new TestVM(js, sb);
   147         } catch (Exception ex) {
   148             if (sb.length() > 2000) {
   149                 sb = dumpJS(sb);
   150             }
   151             fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
   152             return null;
   153         }
   154     }
   155 
   156     Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException {
   157         return getCode().invokeMethod(bck2brwsr, "loadClass", LoopControl.class.getName());
   158     }
   159     
   160     Object invokeMethod(Object obj, String method, Object... params) throws ScriptException, NoSuchMethodException {
   161         return getCode().invokeMethod(obj, method, params);
   162     }
   163 
   164     Object invokeFunction(String methodName, Object... args) throws ScriptException, NoSuchMethodException {
   165         return getCode().invokeFunction(methodName, args);
   166     }
   167 
   168     static StringBuilder dumpJS(CharSequence sb) throws IOException {
   169         File f = File.createTempFile("execution", ".js");
   170         Writer w = new OutputStreamWriter(new FileOutputStream(f), "UTF-8");
   171         w.append(sb);
   172         w.close();
   173         return new StringBuilder(f.getPath());
   174     }
   175 
   176     @Override
   177     public String toString() {
   178         try {
   179             return dumpJS(codeSeq).toString();
   180         } catch (IOException ex) {
   181             return ex.toString();
   182         }
   183     }
   184 
   185     final CharSequence codeSeq() {
   186         return codeSeq;
   187     }
   188 
   189     /**
   190      * @return the code
   191      */
   192     private Invocable getCode() throws ScriptException {
   193         if (code == null) {
   194             Object res = js.eval(code.toString());
   195             code = (Invocable) js;
   196             this.bck2brwsr = ((ScriptEngine)code).eval("bck2brwsr(function(n) { return loader.get(n); })");
   197             ((ScriptEngine)code).getContext().setAttribute("loader", this, ScriptContext.ENGINE_SCOPE);
   198         }
   199         return (Invocable)code;
   200     }
   201     
   202     private static class EmulationResources implements Bck2Brwsr.Resources {
   203         @Override
   204         public InputStream get(String name) throws IOException {
   205             if ("java/net/URI.class".equals(name)) {
   206                 // skip
   207                 return null;
   208             }
   209             if ("java/net/URLConnection.class".equals(name)) {
   210                 // skip
   211                 return null;
   212             }
   213             if ("java/lang/System.class".equals(name)) {
   214                 // skip
   215                 return null;
   216             }
   217             if ("java/io/PrintStream.class".equals(name)) {
   218                 // skip
   219                 return null;
   220             }
   221             if ("java/io/PrintWriter.class".equals(name)) {
   222                 // skip
   223                 return null;
   224             }
   225             Enumeration<URL> en = LoopControlTest.class.getClassLoader().getResources(name);
   226             URL u = null;
   227             while (en.hasMoreElements()) {
   228                 u = en.nextElement();
   229             }
   230             if (u == null) {
   231                 throw new IOException("Can't find " + name);
   232             }
   233             if (u.toExternalForm().contains("rt.jar!")) {
   234                 throw new IOException("No emulation for " + u);
   235             }
   236             return u.openStream();
   237         }
   238     }
   239 }