Defining API for registration of a Flow.Analyzer and getting ready for use the one from Graal
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/flow/pom.xml Wed Mar 11 18:58:39 2015 +0100
1.3 @@ -0,0 +1,37 @@
1.4 +<?xml version="1.0" encoding="UTF-8"?>
1.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">
1.6 + <modelVersion>4.0.0</modelVersion>
1.7 + <parent>
1.8 + <groupId>org.apidesign.bck2brwsr</groupId>
1.9 + <artifactId>rt</artifactId>
1.10 + <version>1.0-SNAPSHOT</version>
1.11 + </parent>
1.12 + <artifactId>flow</artifactId>
1.13 + <name>Graal Flow Analyzer</name>
1.14 + <packaging>jar</packaging>
1.15 + <dependencies>
1.16 + <dependency>
1.17 + <groupId>org.testng</groupId>
1.18 + <artifactId>testng</artifactId>
1.19 + <scope>test</scope>
1.20 + </dependency>
1.21 + <!--
1.22 + <dependency>
1.23 + <groupId>com.oracle.graal</groupId>
1.24 + <artifactId>tag</artifactId>
1.25 + <version>0.6-SNAPSHOT</version>
1.26 + </dependency>
1.27 + -->
1.28 + <dependency>
1.29 + <groupId>${project.groupId}</groupId>
1.30 + <artifactId>vm4brwsr</artifactId>
1.31 + <version>${project.version}</version>
1.32 + </dependency>
1.33 + <dependency>
1.34 + <groupId>${project.groupId}</groupId>
1.35 + <artifactId>emul.mini</artifactId>
1.36 + <version>${project.version}</version>
1.37 + <scope>test</scope>
1.38 + </dependency>
1.39 + </dependencies>
1.40 +</project>
1.41 \ No newline at end of file
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/rt/flow/src/main/java/org/apidesign/bck2brwsr/flow/GraalFlowAnalyzer.java Wed Mar 11 18:58:39 2015 +0100
2.3 @@ -0,0 +1,46 @@
2.4 +/**
2.5 + * Back 2 Browser Bytecode Translator
2.6 + * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
2.7 + *
2.8 + * This program is free software: you can redistribute it and/or modify
2.9 + * it under the terms of the GNU General Public License as published by
2.10 + * the Free Software Foundation, version 2 of the License.
2.11 + *
2.12 + * This program is distributed in the hope that it will be useful,
2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.15 + * GNU General Public License for more details.
2.16 + *
2.17 + * You should have received a copy of the GNU General Public License
2.18 + * along with this program. Look for COPYING file in the top folder.
2.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
2.20 + */
2.21 +package org.apidesign.bck2brwsr.flow;
2.22 +
2.23 +import org.apidesign.vm4brwsr.Bck2Brwsr;
2.24 +
2.25 +/** Poweful flow analyzer. Based on internals used by Graal JVM
2.26 + * compiler.
2.27 + *
2.28 + * @author Jaroslav Tulach
2.29 + */
2.30 +public final class GraalFlowAnalyzer {
2.31 + private GraalFlowAnalyzer() {
2.32 + }
2.33 +
2.34 + public static Bck2Brwsr.Flow.Analyzer getDefault() {
2.35 + return Impl.DEFAULT;
2.36 + }
2.37 +
2.38 + private static class Impl implements Bck2Brwsr.Flow.Analyzer {
2.39 + static Impl DEFAULT = new Impl();
2.40 +
2.41 + private Impl() {
2.42 + }
2.43 +
2.44 + @Override
2.45 + public boolean analyze(Bck2Brwsr.Flow result) {
2.46 + return false;
2.47 + }
2.48 + }
2.49 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/LoopControl.java Wed Mar 11 18:58:39 2015 +0100
3.3 @@ -0,0 +1,32 @@
3.4 +/**
3.5 + * Back 2 Browser Bytecode Translator
3.6 + * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.7 + *
3.8 + * This program is free software: you can redistribute it and/or modify
3.9 + * it under the terms of the GNU General Public License as published by
3.10 + * the Free Software Foundation, version 2 of the License.
3.11 + *
3.12 + * This program is distributed in the hope that it will be useful,
3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.15 + * GNU General Public License for more details.
3.16 + *
3.17 + * You should have received a copy of the GNU General Public License
3.18 + * along with this program. Look for COPYING file in the top folder.
3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
3.20 + */
3.21 +package org.apidesign.bck2brwsr.flow;
3.22 +
3.23 +public class LoopControl {
3.24 + public static int simpleLoopTestWithExit(int i) {
3.25 + int sum = 0;
3.26 + for (int j = 0; j < i; j++) {
3.27 + sum += j;
3.28 + }
3.29 + if (i == 123) {
3.30 + return 123123 + sum;
3.31 + } else {
3.32 + return i * 2 + sum;
3.33 + }
3.34 + }
3.35 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/LoopControlTest.java Wed Mar 11 18:58:39 2015 +0100
4.3 @@ -0,0 +1,77 @@
4.4 +/**
4.5 + * Back 2 Browser Bytecode Translator
4.6 + * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.7 + *
4.8 + * This program is free software: you can redistribute it and/or modify
4.9 + * it under the terms of the GNU General Public License as published by
4.10 + * the Free Software Foundation, version 2 of the License.
4.11 + *
4.12 + * This program is distributed in the hope that it will be useful,
4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.15 + * GNU General Public License for more details.
4.16 + *
4.17 + * You should have received a copy of the GNU General Public License
4.18 + * along with this program. Look for COPYING file in the top folder.
4.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
4.20 + */
4.21 +package org.apidesign.bck2brwsr.flow;
4.22 +
4.23 +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow;
4.24 +import static org.testng.Assert.*;
4.25 +import org.testng.annotations.AfterClass;
4.26 +import org.testng.annotations.AfterMethod;
4.27 +import org.testng.annotations.BeforeClass;
4.28 +import org.testng.annotations.BeforeMethod;
4.29 +import org.testng.annotations.Test;
4.30 +
4.31 +/**
4.32 + *
4.33 + * @author Jaroslav Tulach
4.34 + */
4.35 +public class LoopControlTest {
4.36 + private static TestVM vm;
4.37 +
4.38 + public LoopControlTest() {
4.39 + }
4.40 +
4.41 + @BeforeClass
4.42 + public static void setUpClass() throws Exception {
4.43 + class MyFlow implements Flow.Analyzer {
4.44 + boolean called;
4.45 + @Override
4.46 + public boolean analyze(Flow request) {
4.47 + called = true;
4.48 + return GraalFlowAnalyzer.getDefault().analyze(request);
4.49 + }
4.50 + }
4.51 + MyFlow flow = new MyFlow();
4.52 + vm = TestVM.compileClass(null, flow, LoopControl.class.getName().replace('.', '/'));
4.53 + assertTrue(flow.called, "We have been consuted about at least one method");
4.54 + }
4.55 +
4.56 + @AfterClass
4.57 + public static void tearDownClass() throws Exception {
4.58 + vm = null;
4.59 + }
4.60 +
4.61 + @BeforeMethod
4.62 + public void setUpMethod() throws Exception {
4.63 + }
4.64 +
4.65 + @AfterMethod
4.66 + public void tearDownMethod() throws Exception {
4.67 + }
4.68 +
4.69 + @Test
4.70 + public void testExecute() throws Exception {
4.71 + String code = vm.codeSeq().toString();
4.72 + int begin = code.indexOf("simpleLoopTestWithExit__II = function");
4.73 + assertNotEquals(begin, -1, "Control loop defined" + code);
4.74 + int end = code.indexOf("m.access = ", begin);
4.75 + assertNotEquals(end, -1, "Control loop end defined" + code);
4.76 + final String body = code.substring(begin, end);
4.77 + assertFalse(body.contains("gt"), "No gt control flow used: " + body);
4.78 + }
4.79 +
4.80 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/TestVM.java Wed Mar 11 18:58:39 2015 +0100
5.3 @@ -0,0 +1,320 @@
5.4 +/**
5.5 + * Back 2 Browser Bytecode Translator
5.6 + * Copyright (C) 2012-2015 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.flow;
5.22 +
5.23 +import java.io.ByteArrayInputStream;
5.24 +import java.io.File;
5.25 +import java.io.FileOutputStream;
5.26 +import java.io.IOException;
5.27 +import java.io.InputStream;
5.28 +import java.io.OutputStreamWriter;
5.29 +import java.io.Writer;
5.30 +import java.net.URL;
5.31 +import java.util.ArrayList;
5.32 +import java.util.Arrays;
5.33 +import java.util.Enumeration;
5.34 +import java.util.HashSet;
5.35 +import java.util.List;
5.36 +import java.util.Set;
5.37 +import javax.script.Invocable;
5.38 +import javax.script.ScriptContext;
5.39 +import javax.script.ScriptEngine;
5.40 +import javax.script.ScriptEngineManager;
5.41 +import javax.script.ScriptException;
5.42 +import org.apidesign.vm4brwsr.Bck2Brwsr;
5.43 +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow;
5.44 +import org.apidesign.vm4brwsr.ObfuscationLevel;
5.45 +import static org.testng.Assert.*;
5.46 +
5.47 +public final class TestVM {
5.48 + private final Invocable code;
5.49 + private final CharSequence codeSeq;
5.50 + private final Object bck2brwsr;
5.51 +
5.52 +
5.53 + private TestVM(Invocable code, CharSequence codeSeq) throws ScriptException, NoSuchMethodException {
5.54 + this.code = code;
5.55 + this.codeSeq = codeSeq;
5.56 + this.bck2brwsr = ((ScriptEngine)code).eval("bck2brwsr(function(n) { return loader.get(n); })");
5.57 + ((ScriptEngine)code).getContext().setAttribute("loader", this, ScriptContext.ENGINE_SCOPE);
5.58 + }
5.59 +
5.60 + public Object execCode(
5.61 + String msg, Class<?> clazz, String method,
5.62 + Object expRes, Object... args
5.63 + ) throws Exception {
5.64 + Object ret = null;
5.65 + try {
5.66 + ret = code.invokeMethod(bck2brwsr, "loadClass", clazz.getName());
5.67 + List<Object> ma = new ArrayList<Object>();
5.68 + ma.add(method);
5.69 + ma.addAll(Arrays.asList(args));
5.70 + ret = code.invokeMethod(ret, "invoke", ma.toArray());
5.71 + } catch (ScriptException ex) {
5.72 + fail("Execution failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
5.73 + } catch (NoSuchMethodException ex) {
5.74 + fail("Cannot find method in " + dumpJS(codeSeq), ex);
5.75 + }
5.76 + if (ret == null && expRes == null) {
5.77 + return null;
5.78 + }
5.79 + if (expRes != null && expRes.equals(ret)) {
5.80 + return null;
5.81 + }
5.82 + if (expRes instanceof Number) {
5.83 + // in case of Long it is necessary convert it to number
5.84 + // since the Long is represented by two numbers in JavaScript
5.85 + try {
5.86 + final Object toFP = ((ScriptEngine)code).eval("Number.prototype.toFP");
5.87 + if (ret instanceof Long) {
5.88 + ret = code.invokeMethod(toFP, "call", ret);
5.89 + }
5.90 + ret = code.invokeFunction("Number", ret);
5.91 + } catch (ScriptException ex) {
5.92 + fail("Conversion to number failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
5.93 + } catch (NoSuchMethodException ex) {
5.94 + fail("Cannot find global Number(x) function in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex);
5.95 + }
5.96 + }
5.97 + return ret;
5.98 + }
5.99 +
5.100 + void assertExec(
5.101 + String msg, Class clazz, String method, Object expRes, Object... args
5.102 + ) throws Exception {
5.103 + Object ret = execCode(msg, clazz, method, expRes, args);
5.104 + if (ret == null) {
5.105 + return;
5.106 + }
5.107 + if (expRes instanceof Integer && ret instanceof Double) {
5.108 + expRes = ((Integer)expRes).doubleValue();
5.109 + }
5.110 + if (expRes != null && expRes.equals(ret)) {
5.111 + return;
5.112 + }
5.113 + assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(codeSeq));
5.114 + }
5.115 +
5.116 + static TestVM compileClass(String... names) throws ScriptException, IOException {
5.117 + return compileClass(null, GraalFlowAnalyzer.getDefault(), names);
5.118 + }
5.119 +
5.120 + static TestVM compileClass(StringBuilder sb, Flow.Analyzer flow, String... names) throws ScriptException, IOException {
5.121 + return compileClass(sb, null, flow, names);
5.122 + }
5.123 +
5.124 + static TestVM compileClass(StringBuilder sb,
5.125 + ScriptEngine[] eng, Flow.Analyzer flow, String... names
5.126 + ) throws ScriptException, IOException {
5.127 + return compileClass(sb, eng, flow, new EmulationResources(), names);
5.128 + }
5.129 + static TestVM compileClass(
5.130 + StringBuilder sb,
5.131 + ScriptEngine[] eng,
5.132 + Flow.Analyzer flow,
5.133 + Bck2Brwsr.Resources resources,
5.134 + String... names
5.135 + ) throws ScriptException, IOException {
5.136 + if (sb == null) {
5.137 + sb = new StringBuilder();
5.138 + }
5.139 + Bck2Brwsr.newCompiler()
5.140 + .resources(resources)
5.141 + .addClasses(names)
5.142 + .flowAnalyzer(flow)
5.143 + .generate(sb);
5.144 + ScriptEngineManager sem = new ScriptEngineManager();
5.145 + ScriptEngine js = sem.getEngineByExtension("js");
5.146 + if (eng != null) {
5.147 + eng[0] = js;
5.148 + }
5.149 + try {
5.150 + Object res = js.eval(sb.toString());
5.151 + assertTrue(js instanceof Invocable, "It is invocable object: " + res);
5.152 + return new TestVM((Invocable) js, sb);
5.153 + } catch (Exception ex) {
5.154 + if (sb.length() > 2000) {
5.155 + sb = dumpJS(sb);
5.156 + }
5.157 + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
5.158 + return null;
5.159 + }
5.160 + }
5.161 +
5.162 + static TestVM compileClassAsExtension(
5.163 + StringBuilder sb, ScriptEngine[] eng,
5.164 + String name, final String resourceName, final String resourceContent
5.165 + ) throws ScriptException, IOException {
5.166 + return compileClassesAsExtension(sb, eng, resourceName, resourceContent, name);
5.167 + }
5.168 + static TestVM compileClassesAsExtension(
5.169 + StringBuilder sb, ScriptEngine[] eng,
5.170 + final String resourceName, final String resourceContent, String... names
5.171 + ) throws ScriptException, IOException {
5.172 + if (sb == null) {
5.173 + sb = new StringBuilder();
5.174 + }
5.175 + if (eng[0] == null) {
5.176 + ScriptEngineManager sem = new ScriptEngineManager();
5.177 + ScriptEngine js = sem.getEngineByExtension("js");
5.178 + eng[0] = js;
5.179 + Bck2Brwsr.newCompiler().resources(new EmulationResources())
5.180 + .obfuscation(ObfuscationLevel.NONE).generate(sb);
5.181 + }
5.182 + Set<String> exp = new HashSet<String>();
5.183 + for (String n : names) {
5.184 + int last = n.lastIndexOf('/');
5.185 + exp.add(n.substring(0, last + 1));
5.186 + }
5.187 + Bck2Brwsr b2b = Bck2Brwsr.newCompiler().
5.188 + resources(new EmulationResources() {
5.189 + @Override
5.190 + public InputStream get(String name) throws IOException {
5.191 + if (name.equals(resourceName)) {
5.192 + return new ByteArrayInputStream(resourceContent.getBytes("UTF-8"));
5.193 + }
5.194 + return super.get(name);
5.195 + }
5.196 + }).
5.197 + addClasses(names).
5.198 + addResources("org/apidesign/vm4brwsr/obj.js").
5.199 + addExported(exp.toArray(new String[0])).
5.200 + obfuscation(ObfuscationLevel.FULL).
5.201 + library();
5.202 + if (resourceName != null) {
5.203 + b2b = b2b.addResources(resourceName);
5.204 + }
5.205 + b2b.generate(sb);
5.206 + try {
5.207 + defineAtoB(eng[0]);
5.208 + Object res = eng[0].eval(sb.toString());
5.209 + assertTrue(eng[0] instanceof Invocable, "It is invocable object: " + res);
5.210 + return new TestVM((Invocable) eng[0], sb);
5.211 + } catch (Exception ex) {
5.212 + if (sb.length() > 2000) {
5.213 + sb = dumpJS(sb);
5.214 + }
5.215 + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
5.216 + return null;
5.217 + }
5.218 + }
5.219 +
5.220 + static TestVM compileClassAndResources(StringBuilder sb, ScriptEngine[] eng, String name, String... resources) throws ScriptException, IOException {
5.221 + if (sb == null) {
5.222 + sb = new StringBuilder();
5.223 + }
5.224 + Bck2Brwsr b2b = Bck2Brwsr.newCompiler().
5.225 + resources(new EmulationResources()).
5.226 + addRootClasses(name).
5.227 + addResources(resources);
5.228 + b2b.generate(sb);
5.229 + ScriptEngineManager sem = new ScriptEngineManager();
5.230 + ScriptEngine js = sem.getEngineByExtension("js");
5.231 + if (eng != null) {
5.232 + eng[0] = js;
5.233 + }
5.234 + try {
5.235 + defineAtoB(js);
5.236 +
5.237 + Object res = js.eval(sb.toString());
5.238 + assertTrue(js instanceof Invocable, "It is invocable object: " + res);
5.239 + return new TestVM((Invocable) js, sb);
5.240 + } catch (Exception ex) {
5.241 + if (sb.length() > 2000) {
5.242 + sb = dumpJS(sb);
5.243 + }
5.244 + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex);
5.245 + return null;
5.246 + }
5.247 + }
5.248 +
5.249 + private static void defineAtoB(ScriptEngine js) throws ScriptException {
5.250 + js.eval("atob = function(s) { return new String(org.apidesign.vm4brwsr.ResourcesTest.parseBase64Binary(s)); }");
5.251 + }
5.252 +
5.253 + Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException {
5.254 + return code.invokeMethod(bck2brwsr, "loadClass", LoopControl.class.getName());
5.255 + }
5.256 +
5.257 + Object invokeMethod(Object obj, String method, Object... params) throws ScriptException, NoSuchMethodException {
5.258 + return code.invokeMethod(obj, method, params);
5.259 + }
5.260 +
5.261 + Object invokeFunction(String methodName, Object... args) throws ScriptException, NoSuchMethodException {
5.262 + return code.invokeFunction(methodName, args);
5.263 + }
5.264 +
5.265 + static StringBuilder dumpJS(CharSequence sb) throws IOException {
5.266 + File f = File.createTempFile("execution", ".js");
5.267 + Writer w = new OutputStreamWriter(new FileOutputStream(f), "UTF-8");
5.268 + w.append(sb);
5.269 + w.close();
5.270 + return new StringBuilder(f.getPath());
5.271 + }
5.272 +
5.273 + @Override
5.274 + public String toString() {
5.275 + try {
5.276 + return dumpJS(codeSeq).toString();
5.277 + } catch (IOException ex) {
5.278 + return ex.toString();
5.279 + }
5.280 + }
5.281 +
5.282 + final CharSequence codeSeq() {
5.283 + return codeSeq;
5.284 + }
5.285 +
5.286 + private static class EmulationResources implements Bck2Brwsr.Resources {
5.287 + @Override
5.288 + public InputStream get(String name) throws IOException {
5.289 + if ("java/net/URI.class".equals(name)) {
5.290 + // skip
5.291 + return null;
5.292 + }
5.293 + if ("java/net/URLConnection.class".equals(name)) {
5.294 + // skip
5.295 + return null;
5.296 + }
5.297 + if ("java/lang/System.class".equals(name)) {
5.298 + // skip
5.299 + return null;
5.300 + }
5.301 + if ("java/io/PrintStream.class".equals(name)) {
5.302 + // skip
5.303 + return null;
5.304 + }
5.305 + if ("java/io/PrintWriter.class".equals(name)) {
5.306 + // skip
5.307 + return null;
5.308 + }
5.309 + Enumeration<URL> en = LoopControlTest.class.getClassLoader().getResources(name);
5.310 + URL u = null;
5.311 + while (en.hasMoreElements()) {
5.312 + u = en.nextElement();
5.313 + }
5.314 + if (u == null) {
5.315 + throw new IOException("Can't find " + name);
5.316 + }
5.317 + if (u.toExternalForm().contains("rt.jar!")) {
5.318 + throw new IOException("No emulation for " + u);
5.319 + }
5.320 + return u.openStream();
5.321 + }
5.322 + }
5.323 +}
6.1 --- a/rt/mojo/pom.xml Wed Mar 11 18:41:12 2015 +0100
6.2 +++ b/rt/mojo/pom.xml Wed Mar 11 18:58:39 2015 +0100
6.3 @@ -104,5 +104,17 @@
6.4 <version>1.0-SNAPSHOT</version>
6.5 <type>jar</type>
6.6 </dependency>
6.7 + <dependency>
6.8 + <groupId>com.oracle.graal</groupId>
6.9 + <artifactId>tag</artifactId>
6.10 + <version>0.6-SNAPSHOT</version>
6.11 + <type>jar</type>
6.12 + </dependency>
6.13 + <dependency>
6.14 + <groupId>org.testng</groupId>
6.15 + <artifactId>testng</artifactId>
6.16 + <version>6.8.1</version>
6.17 + <scope>test</scope>
6.18 + </dependency>
6.19 </dependencies>
6.20 </project>
7.1 --- a/rt/pom.xml Wed Mar 11 18:41:12 2015 +0100
7.2 +++ b/rt/pom.xml Wed Mar 11 18:58:39 2015 +0100
7.3 @@ -19,6 +19,7 @@
7.4 <module>vmtest</module>
7.5 <module>aot</module>
7.6 <module>aot-nb-test</module>
7.7 + <module>flow</module>
7.8 </modules>
7.9 <profiles>
7.10 <profile>
8.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Wed Mar 11 18:41:12 2015 +0100
8.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Wed Mar 11 18:58:39 2015 +0100
8.3 @@ -82,12 +82,12 @@
8.4 private final Resources res;
8.5 private final Boolean extension;
8.6 private final StringArray classpath;
8.7 + private final Flow.Analyzer flow;
8.8
8.9 private Bck2Brwsr(
8.10 - ObfuscationLevel level,
8.11 - StringArray exported, StringArray classes, StringArray resources,
8.12 - Resources res,
8.13 - Boolean extension, StringArray classpath
8.14 + ObfuscationLevel level, StringArray exported,
8.15 + StringArray classes, StringArray resources, Resources res,
8.16 + Boolean extension, StringArray classpath, Flow.Analyzer flow
8.17 ) {
8.18 this.level = level;
8.19 this.exported = exported;
8.20 @@ -96,6 +96,7 @@
8.21 this.res = res;
8.22 this.extension = extension;
8.23 this.classpath = classpath;
8.24 + this.flow = flow;
8.25 }
8.26
8.27 /** Helper method to generate virtual machine from bytes served by a <code>resources</code>
8.28 @@ -135,7 +136,7 @@
8.29 return new Bck2Brwsr(
8.30 ObfuscationLevel.NONE,
8.31 new StringArray(), new StringArray(), new StringArray(),
8.32 - null, false, null
8.33 + null, false, null, null
8.34 );
8.35 }
8.36
8.37 @@ -156,7 +157,7 @@
8.38 public Bck2Brwsr addExported(String... exported) {
8.39 return new Bck2Brwsr(
8.40 level, this.exported.addAndNew(exported),
8.41 - classes, resources, res, extension, classpath
8.42 + classes, resources, res, extension, classpath, flow
8.43 );
8.44 }
8.45
8.46 @@ -197,7 +198,7 @@
8.47 } else {
8.48 return new Bck2Brwsr(level, exported,
8.49 this.classes.addAndNew(classes), resources, res,
8.50 - extension, classpath);
8.51 + extension, classpath, flow);
8.52 }
8.53 }
8.54
8.55 @@ -217,7 +218,7 @@
8.56 return this;
8.57 } else {
8.58 return new Bck2Brwsr(level, exported, this.classes,
8.59 - this.resources.addAndNew(resources), res, extension, classpath
8.60 + this.resources.addAndNew(resources), res, extension, classpath, flow
8.61 );
8.62 }
8.63 }
8.64 @@ -231,7 +232,7 @@
8.65 * @since 0.5
8.66 */
8.67 public Bck2Brwsr obfuscation(ObfuscationLevel level) {
8.68 - return new Bck2Brwsr(level, exported, classes, resources, res, extension, classpath);
8.69 + return new Bck2Brwsr(level, exported, classes, resources, res, extension, classpath, flow);
8.70 }
8.71
8.72 /** A way to change the provider of additional resources (classes) for the
8.73 @@ -245,7 +246,7 @@
8.74 public Bck2Brwsr resources(Resources res) {
8.75 return new Bck2Brwsr(
8.76 level, exported, classes, resources,
8.77 - res, extension, classpath
8.78 + res, extension, classpath, flow
8.79 );
8.80 }
8.81
8.82 @@ -272,7 +273,7 @@
8.83 return new Bck2Brwsr(
8.84 level, exported, classes,
8.85 resources, res, true,
8.86 - StringArray.asList(classpath)
8.87 + StringArray.asList(classpath), flow
8.88 );
8.89 }
8.90
8.91 @@ -289,7 +290,7 @@
8.92 public Bck2Brwsr standalone(boolean includeVM) {
8.93 return new Bck2Brwsr(
8.94 level, exported, classes, resources,
8.95 - res, includeVM ? false : null, null
8.96 + res, includeVM ? false : null, null, flow
8.97 );
8.98 }
8.99
8.100 @@ -319,6 +320,21 @@
8.101 return resources(new LdrRsrcs(loader, ignoreBootClassPath));
8.102 }
8.103
8.104 + /** A way to register flow analyzer. Such analyzer can optimize the
8.105 + * representation of cycles inside of method bodies.
8.106 + *
8.107 + * @param flow the analyzer to be consulted with each method body
8.108 + * @return new instance of the compiler with all values being the same, just
8.109 + * different flow analyzer
8.110 + * @since 0.15
8.111 + */
8.112 + public Bck2Brwsr flowAnalyzer(Flow.Analyzer flow) {
8.113 + return new Bck2Brwsr(
8.114 + level, exported, classes, resources, res,
8.115 + extension, classpath, flow
8.116 + );
8.117 + }
8.118 +
8.119 /** Generates virtual machine based on previous configuration of the
8.120 * compiler.
8.121 *
8.122 @@ -373,6 +389,10 @@
8.123 StringArray classpath() {
8.124 return classpath;
8.125 }
8.126 +
8.127 + Flow.Analyzer flow() {
8.128 + return flow;
8.129 + }
8.130
8.131 /** Provider of resources (classes and other files). The
8.132 * {@link #generate(java.lang.Appendable, org.apidesign.vm4brwsr.Bck2Brwsr.Resources, java.lang.String[])
8.133 @@ -391,4 +411,51 @@
8.134 */
8.135 public InputStream get(String resource) throws IOException;
8.136 }
8.137 +
8.138 + /** Represents control flow inside single method.
8.139 + * Passed into {@link Analyzer#analyze(byte[], org.apidesign.vm4brwsr.Bck2Brwsr.Flow)}
8.140 + * method that can be registed via {@link Bck2Brwsr#flowAnalyzer(org.apidesign.vm4brwsr.Bck2Brwsr.Flow.Analyzer)}
8.141 + * method.
8.142 + *
8.143 + * @since 0.15
8.144 + */
8.145 + public static final class Flow {
8.146 + private final byte[] byteCode;
8.147 + Flow(byte[] byteCode) {
8.148 + this.byteCode = byteCode;
8.149 + }
8.150 +
8.151 + /** Access to bytecode of the method to analyse.
8.152 + *
8.153 + * @return unmodifiable bytecode of the instructions in the method body
8.154 + */
8.155 + public byte[] getMethodByteCode() {
8.156 + return byteCode;
8.157 + }
8.158 +
8.159 + public void registerCycle(int offset) {
8.160 + }
8.161 +
8.162 + public void registerIf(int offset) {
8.163 + }
8.164 +
8.165 + /** Provider of advanced analysis of the code flow inside of
8.166 + * method bodies. Register via {@link Bck2Brwsr#flowAnalyzer(org.apidesign.vm4brwsr.Bck2Brwsr.Flow.Analyzer)}
8.167 + * when constructing the {@link Bck2Brwsr#newCompiler() compiler}.
8.168 + *
8.169 + * @since 0.15
8.170 + */
8.171 + public interface Analyzer {
8.172 + /** Called to analyze method bodies and offer better control flow.
8.173 + *
8.174 + *
8.175 + * @param request flow computation request and also a
8.176 + * callback interface with methods to define the flow
8.177 + * @return <code>true</code> if the analysis was successful,
8.178 + * <code>false</code> otherwise
8.179 + */
8.180 + public boolean analyze(Flow request);
8.181 + }
8.182 + }
8.183 +
8.184 }
9.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Mar 11 18:41:12 2015 +0100
9.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Mar 11 18:58:39 2015 +0100
9.3 @@ -20,6 +20,7 @@
9.4 import java.io.IOException;
9.5 import java.io.InputStream;
9.6 import org.apidesign.bck2brwsr.core.JavaScriptBody;
9.7 +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow;
9.8 import static org.apidesign.vm4brwsr.ByteCodeParser.*;
9.9
9.10 /** Translator of the code inside class files to JavaScript.
9.11 @@ -449,6 +450,7 @@
9.12 }
9.13 return defineProp;
9.14 }
9.15 + Flow flow = checkFlow(byteCodes);
9.16
9.17 final StackMapper smapper = new StackMapper();
9.18
9.19 @@ -2466,4 +2468,8 @@
9.20 private static void println(String msg) {
9.21 System.err.println(msg);
9.22 }
9.23 +
9.24 + protected Flow checkFlow(byte[] byteCodes) {
9.25 + return null;
9.26 + }
9.27 }
10.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Wed Mar 11 18:41:12 2015 +0100
10.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Wed Mar 11 18:58:39 2015 +0100
10.3 @@ -20,6 +20,7 @@
10.4 import java.io.IOException;
10.5 import java.io.InputStream;
10.6 import org.apidesign.bck2brwsr.core.JavaScriptBody;
10.7 +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow;
10.8 import org.apidesign.vm4brwsr.ByteCodeParser.ClassData;
10.9 import org.apidesign.vm4brwsr.ByteCodeParser.FieldData;
10.10 import org.apidesign.vm4brwsr.ByteCodeParser.MethodData;
10.11 @@ -35,11 +36,12 @@
10.12 private final ExportedSymbols exportedSymbols;
10.13 private final StringBuilder invokerMethods;
10.14 private final StringArray asBinary;
10.15 + private final Flow.Analyzer flow;
10.16 int exportedCount;
10.17
10.18 private VM(
10.19 Appendable out, Bck2Brwsr.Resources resources,
10.20 - StringArray explicitlyExported, StringArray asBinary
10.21 + StringArray explicitlyExported, StringArray asBinary, Flow.Analyzer flow
10.22 ) {
10.23 super(out);
10.24 this.resources = resources;
10.25 @@ -47,6 +49,7 @@
10.26 this.exportedSymbols = new ExportedSymbols(resources, explicitlyExported);
10.27 this.invokerMethods = new StringBuilder();
10.28 this.asBinary = asBinary;
10.29 + this.flow = flow;
10.30 }
10.31
10.32 static {
10.33 @@ -79,7 +82,8 @@
10.34 fixedNames.add(VM.class.getName().replace('.', '/'));
10.35 vm = new Extension(out,
10.36 config.getResources(), both, config.exported(),
10.37 - config.allResources(), config.classpath()
10.38 + config.allResources(), config.classpath(),
10.39 + config.flow()
10.40 );
10.41 } else {
10.42 if (config.includeVM()) {
10.43 @@ -87,7 +91,8 @@
10.44 }
10.45 vm = new Standalone(out,
10.46 config.getResources(), config.exported(),
10.47 - config.allResources()
10.48 + config.allResources(),
10.49 + config.flow()
10.50 );
10.51 }
10.52 vm.doCompile(fixedNames.addAndNew(both));
10.53 @@ -419,6 +424,15 @@
10.54 return object + "." + mangledName;
10.55 }
10.56
10.57 + @Override
10.58 + protected Flow checkFlow(byte[] byteCodes) {
10.59 + if (flow == null) {
10.60 + return null;
10.61 + }
10.62 + Flow f = new Flow(byteCodes);
10.63 + return flow.analyze(f) ? f : null;
10.64 + }
10.65 +
10.66 private final class ExportedMethodFinder
10.67 implements ClassDataCache.TraversalCallback<MethodData> {
10.68 private final ExportedSymbols exportedSymbols;
10.69 @@ -452,9 +466,10 @@
10.70 private static final class Standalone extends VM {
10.71 private Standalone(Appendable out,
10.72 Bck2Brwsr.Resources resources,
10.73 - StringArray explicitlyExported, StringArray asBinary
10.74 + StringArray explicitlyExported, StringArray asBinary,
10.75 + Flow.Analyzer flow
10.76 ) {
10.77 - super(out, resources, explicitlyExported, asBinary);
10.78 + super(out, resources, explicitlyExported, asBinary, flow);
10.79 }
10.80
10.81 @Override
10.82 @@ -700,9 +715,9 @@
10.83
10.84 private Extension(Appendable out, Bck2Brwsr.Resources resources,
10.85 String[] extClassesArray, StringArray explicitlyExported,
10.86 - StringArray asBinary, StringArray classpath
10.87 + StringArray asBinary, StringArray classpath, Flow.Analyzer flow
10.88 ) throws IOException {
10.89 - super(out, resources, explicitlyExported, asBinary);
10.90 + super(out, resources, explicitlyExported, asBinary, flow);
10.91 this.extensionClasses = StringArray.asList(extClassesArray);
10.92 this.classpath = classpath;
10.93 }