# HG changeset patch # User Jaroslav Tulach # Date 1426096719 -3600 # Node ID 4fef6b767f617f3e0670d43bf5474268ee1852a5 # Parent ff2a253900ed154e7403db87b8770c0f728e064d Defining API for registration of a Flow.Analyzer and getting ready for use the one from Graal diff -r ff2a253900ed -r 4fef6b767f61 rt/flow/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/flow/pom.xml Wed Mar 11 18:58:39 2015 +0100 @@ -0,0 +1,37 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + rt + 1.0-SNAPSHOT + + flow + Graal Flow Analyzer + jar + + + org.testng + testng + test + + + + ${project.groupId} + vm4brwsr + ${project.version} + + + ${project.groupId} + emul.mini + ${project.version} + test + + + \ No newline at end of file diff -r ff2a253900ed -r 4fef6b767f61 rt/flow/src/main/java/org/apidesign/bck2brwsr/flow/GraalFlowAnalyzer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/flow/src/main/java/org/apidesign/bck2brwsr/flow/GraalFlowAnalyzer.java Wed Mar 11 18:58:39 2015 +0100 @@ -0,0 +1,46 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2015 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.flow; + +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** Poweful flow analyzer. Based on internals used by Graal JVM + * compiler. + * + * @author Jaroslav Tulach + */ +public final class GraalFlowAnalyzer { + private GraalFlowAnalyzer() { + } + + public static Bck2Brwsr.Flow.Analyzer getDefault() { + return Impl.DEFAULT; + } + + private static class Impl implements Bck2Brwsr.Flow.Analyzer { + static Impl DEFAULT = new Impl(); + + private Impl() { + } + + @Override + public boolean analyze(Bck2Brwsr.Flow result) { + return false; + } + } +} diff -r ff2a253900ed -r 4fef6b767f61 rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/LoopControl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/LoopControl.java Wed Mar 11 18:58:39 2015 +0100 @@ -0,0 +1,32 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2015 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.flow; + +public class LoopControl { + public static int simpleLoopTestWithExit(int i) { + int sum = 0; + for (int j = 0; j < i; j++) { + sum += j; + } + if (i == 123) { + return 123123 + sum; + } else { + return i * 2 + sum; + } + } +} diff -r ff2a253900ed -r 4fef6b767f61 rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/LoopControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/LoopControlTest.java Wed Mar 11 18:58:39 2015 +0100 @@ -0,0 +1,77 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2015 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.flow; + +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow; +import static org.testng.Assert.*; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class LoopControlTest { + private static TestVM vm; + + public LoopControlTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + class MyFlow implements Flow.Analyzer { + boolean called; + @Override + public boolean analyze(Flow request) { + called = true; + return GraalFlowAnalyzer.getDefault().analyze(request); + } + } + MyFlow flow = new MyFlow(); + vm = TestVM.compileClass(null, flow, LoopControl.class.getName().replace('.', '/')); + assertTrue(flow.called, "We have been consuted about at least one method"); + } + + @AfterClass + public static void tearDownClass() throws Exception { + vm = null; + } + + @BeforeMethod + public void setUpMethod() throws Exception { + } + + @AfterMethod + public void tearDownMethod() throws Exception { + } + + @Test + public void testExecute() throws Exception { + String code = vm.codeSeq().toString(); + int begin = code.indexOf("simpleLoopTestWithExit__II = function"); + assertNotEquals(begin, -1, "Control loop defined" + code); + int end = code.indexOf("m.access = ", begin); + assertNotEquals(end, -1, "Control loop end defined" + code); + final String body = code.substring(begin, end); + assertFalse(body.contains("gt"), "No gt control flow used: " + body); + } + +} diff -r ff2a253900ed -r 4fef6b767f61 rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/TestVM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/flow/src/test/java/org/apidesign/bck2brwsr/flow/TestVM.java Wed Mar 11 18:58:39 2015 +0100 @@ -0,0 +1,320 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012-2015 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.flow; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.script.Invocable; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow; +import org.apidesign.vm4brwsr.ObfuscationLevel; +import static org.testng.Assert.*; + +public final class TestVM { + private final Invocable code; + private final CharSequence codeSeq; + private final Object bck2brwsr; + + + private TestVM(Invocable code, CharSequence codeSeq) throws ScriptException, NoSuchMethodException { + this.code = code; + this.codeSeq = codeSeq; + this.bck2brwsr = ((ScriptEngine)code).eval("bck2brwsr(function(n) { return loader.get(n); })"); + ((ScriptEngine)code).getContext().setAttribute("loader", this, ScriptContext.ENGINE_SCOPE); + } + + public Object execCode( + String msg, Class clazz, String method, + Object expRes, Object... args + ) throws Exception { + Object ret = null; + try { + ret = code.invokeMethod(bck2brwsr, "loadClass", clazz.getName()); + List ma = new ArrayList(); + ma.add(method); + ma.addAll(Arrays.asList(args)); + ret = code.invokeMethod(ret, "invoke", ma.toArray()); + } catch (ScriptException ex) { + fail("Execution failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find method in " + dumpJS(codeSeq), ex); + } + if (ret == null && expRes == null) { + return null; + } + if (expRes != null && expRes.equals(ret)) { + return null; + } + if (expRes instanceof Number) { + // in case of Long it is necessary convert it to number + // since the Long is represented by two numbers in JavaScript + try { + final Object toFP = ((ScriptEngine)code).eval("Number.prototype.toFP"); + if (ret instanceof Long) { + ret = code.invokeMethod(toFP, "call", ret); + } + ret = code.invokeFunction("Number", ret); + } catch (ScriptException ex) { + fail("Conversion to number failed in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find global Number(x) function in " + dumpJS(codeSeq) + ": " + ex.getMessage(), ex); + } + } + return ret; + } + + void assertExec( + String msg, Class clazz, String method, Object expRes, Object... args + ) throws Exception { + Object ret = execCode(msg, clazz, method, expRes, args); + if (ret == null) { + return; + } + if (expRes instanceof Integer && ret instanceof Double) { + expRes = ((Integer)expRes).doubleValue(); + } + if (expRes != null && expRes.equals(ret)) { + return; + } + assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(codeSeq)); + } + + static TestVM compileClass(String... names) throws ScriptException, IOException { + return compileClass(null, GraalFlowAnalyzer.getDefault(), names); + } + + static TestVM compileClass(StringBuilder sb, Flow.Analyzer flow, String... names) throws ScriptException, IOException { + return compileClass(sb, null, flow, names); + } + + static TestVM compileClass(StringBuilder sb, + ScriptEngine[] eng, Flow.Analyzer flow, String... names + ) throws ScriptException, IOException { + return compileClass(sb, eng, flow, new EmulationResources(), names); + } + static TestVM compileClass( + StringBuilder sb, + ScriptEngine[] eng, + Flow.Analyzer flow, + Bck2Brwsr.Resources resources, + String... names + ) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + Bck2Brwsr.newCompiler() + .resources(resources) + .addClasses(names) + .flowAnalyzer(flow) + .generate(sb); + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + if (eng != null) { + eng[0] = js; + } + try { + Object res = js.eval(sb.toString()); + assertTrue(js instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) js, sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex); + return null; + } + } + + static TestVM compileClassAsExtension( + StringBuilder sb, ScriptEngine[] eng, + String name, final String resourceName, final String resourceContent + ) throws ScriptException, IOException { + return compileClassesAsExtension(sb, eng, resourceName, resourceContent, name); + } + static TestVM compileClassesAsExtension( + StringBuilder sb, ScriptEngine[] eng, + final String resourceName, final String resourceContent, String... names + ) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + if (eng[0] == null) { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + eng[0] = js; + Bck2Brwsr.newCompiler().resources(new EmulationResources()) + .obfuscation(ObfuscationLevel.NONE).generate(sb); + } + Set exp = new HashSet(); + for (String n : names) { + int last = n.lastIndexOf('/'); + exp.add(n.substring(0, last + 1)); + } + Bck2Brwsr b2b = Bck2Brwsr.newCompiler(). + resources(new EmulationResources() { + @Override + public InputStream get(String name) throws IOException { + if (name.equals(resourceName)) { + return new ByteArrayInputStream(resourceContent.getBytes("UTF-8")); + } + return super.get(name); + } + }). + addClasses(names). + addResources("org/apidesign/vm4brwsr/obj.js"). + addExported(exp.toArray(new String[0])). + obfuscation(ObfuscationLevel.FULL). + library(); + if (resourceName != null) { + b2b = b2b.addResources(resourceName); + } + b2b.generate(sb); + try { + defineAtoB(eng[0]); + Object res = eng[0].eval(sb.toString()); + assertTrue(eng[0] instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) eng[0], sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex); + return null; + } + } + + static TestVM compileClassAndResources(StringBuilder sb, ScriptEngine[] eng, String name, String... resources) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + Bck2Brwsr b2b = Bck2Brwsr.newCompiler(). + resources(new EmulationResources()). + addRootClasses(name). + addResources(resources); + b2b.generate(sb); + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + if (eng != null) { + eng[0] = js; + } + try { + defineAtoB(js); + + Object res = js.eval(sb.toString()); + assertTrue(js instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) js, sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex); + return null; + } + } + + private static void defineAtoB(ScriptEngine js) throws ScriptException { + js.eval("atob = function(s) { return new String(org.apidesign.vm4brwsr.ResourcesTest.parseBase64Binary(s)); }"); + } + + Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException { + return code.invokeMethod(bck2brwsr, "loadClass", LoopControl.class.getName()); + } + + Object invokeMethod(Object obj, String method, Object... params) throws ScriptException, NoSuchMethodException { + return code.invokeMethod(obj, method, params); + } + + Object invokeFunction(String methodName, Object... args) throws ScriptException, NoSuchMethodException { + return code.invokeFunction(methodName, args); + } + + static StringBuilder dumpJS(CharSequence sb) throws IOException { + File f = File.createTempFile("execution", ".js"); + Writer w = new OutputStreamWriter(new FileOutputStream(f), "UTF-8"); + w.append(sb); + w.close(); + return new StringBuilder(f.getPath()); + } + + @Override + public String toString() { + try { + return dumpJS(codeSeq).toString(); + } catch (IOException ex) { + return ex.toString(); + } + } + + final CharSequence codeSeq() { + return codeSeq; + } + + private static class EmulationResources implements Bck2Brwsr.Resources { + @Override + public InputStream get(String name) throws IOException { + if ("java/net/URI.class".equals(name)) { + // skip + return null; + } + if ("java/net/URLConnection.class".equals(name)) { + // skip + return null; + } + if ("java/lang/System.class".equals(name)) { + // skip + return null; + } + if ("java/io/PrintStream.class".equals(name)) { + // skip + return null; + } + if ("java/io/PrintWriter.class".equals(name)) { + // skip + return null; + } + Enumeration en = LoopControlTest.class.getClassLoader().getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + if (u.toExternalForm().contains("rt.jar!")) { + throw new IOException("No emulation for " + u); + } + return u.openStream(); + } + } +} diff -r ff2a253900ed -r 4fef6b767f61 rt/mojo/pom.xml --- a/rt/mojo/pom.xml Wed Mar 11 18:41:12 2015 +0100 +++ b/rt/mojo/pom.xml Wed Mar 11 18:58:39 2015 +0100 @@ -104,5 +104,17 @@ 1.0-SNAPSHOT jar + + com.oracle.graal + tag + 0.6-SNAPSHOT + jar + + + org.testng + testng + 6.8.1 + test + diff -r ff2a253900ed -r 4fef6b767f61 rt/pom.xml --- a/rt/pom.xml Wed Mar 11 18:41:12 2015 +0100 +++ b/rt/pom.xml Wed Mar 11 18:58:39 2015 +0100 @@ -19,6 +19,7 @@ vmtest aot aot-nb-test + flow diff -r ff2a253900ed -r 4fef6b767f61 rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Wed Mar 11 18:41:12 2015 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Wed Mar 11 18:58:39 2015 +0100 @@ -82,12 +82,12 @@ private final Resources res; private final Boolean extension; private final StringArray classpath; + private final Flow.Analyzer flow; private Bck2Brwsr( - ObfuscationLevel level, - StringArray exported, StringArray classes, StringArray resources, - Resources res, - Boolean extension, StringArray classpath + ObfuscationLevel level, StringArray exported, + StringArray classes, StringArray resources, Resources res, + Boolean extension, StringArray classpath, Flow.Analyzer flow ) { this.level = level; this.exported = exported; @@ -96,6 +96,7 @@ this.res = res; this.extension = extension; this.classpath = classpath; + this.flow = flow; } /** Helper method to generate virtual machine from bytes served by a resources @@ -135,7 +136,7 @@ return new Bck2Brwsr( ObfuscationLevel.NONE, new StringArray(), new StringArray(), new StringArray(), - null, false, null + null, false, null, null ); } @@ -156,7 +157,7 @@ public Bck2Brwsr addExported(String... exported) { return new Bck2Brwsr( level, this.exported.addAndNew(exported), - classes, resources, res, extension, classpath + classes, resources, res, extension, classpath, flow ); } @@ -197,7 +198,7 @@ } else { return new Bck2Brwsr(level, exported, this.classes.addAndNew(classes), resources, res, - extension, classpath); + extension, classpath, flow); } } @@ -217,7 +218,7 @@ return this; } else { return new Bck2Brwsr(level, exported, this.classes, - this.resources.addAndNew(resources), res, extension, classpath + this.resources.addAndNew(resources), res, extension, classpath, flow ); } } @@ -231,7 +232,7 @@ * @since 0.5 */ public Bck2Brwsr obfuscation(ObfuscationLevel level) { - return new Bck2Brwsr(level, exported, classes, resources, res, extension, classpath); + return new Bck2Brwsr(level, exported, classes, resources, res, extension, classpath, flow); } /** A way to change the provider of additional resources (classes) for the @@ -245,7 +246,7 @@ public Bck2Brwsr resources(Resources res) { return new Bck2Brwsr( level, exported, classes, resources, - res, extension, classpath + res, extension, classpath, flow ); } @@ -272,7 +273,7 @@ return new Bck2Brwsr( level, exported, classes, resources, res, true, - StringArray.asList(classpath) + StringArray.asList(classpath), flow ); } @@ -289,7 +290,7 @@ public Bck2Brwsr standalone(boolean includeVM) { return new Bck2Brwsr( level, exported, classes, resources, - res, includeVM ? false : null, null + res, includeVM ? false : null, null, flow ); } @@ -319,6 +320,21 @@ return resources(new LdrRsrcs(loader, ignoreBootClassPath)); } + /** A way to register flow analyzer. Such analyzer can optimize the + * representation of cycles inside of method bodies. + * + * @param flow the analyzer to be consulted with each method body + * @return new instance of the compiler with all values being the same, just + * different flow analyzer + * @since 0.15 + */ + public Bck2Brwsr flowAnalyzer(Flow.Analyzer flow) { + return new Bck2Brwsr( + level, exported, classes, resources, res, + extension, classpath, flow + ); + } + /** Generates virtual machine based on previous configuration of the * compiler. * @@ -373,6 +389,10 @@ StringArray classpath() { return classpath; } + + Flow.Analyzer flow() { + return flow; + } /** Provider of resources (classes and other files). The * {@link #generate(java.lang.Appendable, org.apidesign.vm4brwsr.Bck2Brwsr.Resources, java.lang.String[]) @@ -391,4 +411,51 @@ */ public InputStream get(String resource) throws IOException; } + + /** Represents control flow inside single method. + * Passed into {@link Analyzer#analyze(byte[], org.apidesign.vm4brwsr.Bck2Brwsr.Flow)} + * method that can be registed via {@link Bck2Brwsr#flowAnalyzer(org.apidesign.vm4brwsr.Bck2Brwsr.Flow.Analyzer)} + * method. + * + * @since 0.15 + */ + public static final class Flow { + private final byte[] byteCode; + Flow(byte[] byteCode) { + this.byteCode = byteCode; + } + + /** Access to bytecode of the method to analyse. + * + * @return unmodifiable bytecode of the instructions in the method body + */ + public byte[] getMethodByteCode() { + return byteCode; + } + + public void registerCycle(int offset) { + } + + public void registerIf(int offset) { + } + + /** Provider of advanced analysis of the code flow inside of + * method bodies. Register via {@link Bck2Brwsr#flowAnalyzer(org.apidesign.vm4brwsr.Bck2Brwsr.Flow.Analyzer)} + * when constructing the {@link Bck2Brwsr#newCompiler() compiler}. + * + * @since 0.15 + */ + public interface Analyzer { + /** Called to analyze method bodies and offer better control flow. + * + * + * @param request flow computation request and also a + * callback interface with methods to define the flow + * @return true if the analysis was successful, + * false otherwise + */ + public boolean analyze(Flow request); + } + } + } diff -r ff2a253900ed -r 4fef6b767f61 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Mar 11 18:41:12 2015 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Mar 11 18:58:39 2015 +0100 @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow; import static org.apidesign.vm4brwsr.ByteCodeParser.*; /** Translator of the code inside class files to JavaScript. @@ -449,6 +450,7 @@ } return defineProp; } + Flow flow = checkFlow(byteCodes); final StackMapper smapper = new StackMapper(); @@ -2466,4 +2468,8 @@ private static void println(String msg) { System.err.println(msg); } + + protected Flow checkFlow(byte[] byteCodes) { + return null; + } } diff -r ff2a253900ed -r 4fef6b767f61 rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Wed Mar 11 18:41:12 2015 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Wed Mar 11 18:58:39 2015 +0100 @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.vm4brwsr.Bck2Brwsr.Flow; import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; @@ -35,11 +36,12 @@ private final ExportedSymbols exportedSymbols; private final StringBuilder invokerMethods; private final StringArray asBinary; + private final Flow.Analyzer flow; int exportedCount; private VM( Appendable out, Bck2Brwsr.Resources resources, - StringArray explicitlyExported, StringArray asBinary + StringArray explicitlyExported, StringArray asBinary, Flow.Analyzer flow ) { super(out); this.resources = resources; @@ -47,6 +49,7 @@ this.exportedSymbols = new ExportedSymbols(resources, explicitlyExported); this.invokerMethods = new StringBuilder(); this.asBinary = asBinary; + this.flow = flow; } static { @@ -79,7 +82,8 @@ fixedNames.add(VM.class.getName().replace('.', '/')); vm = new Extension(out, config.getResources(), both, config.exported(), - config.allResources(), config.classpath() + config.allResources(), config.classpath(), + config.flow() ); } else { if (config.includeVM()) { @@ -87,7 +91,8 @@ } vm = new Standalone(out, config.getResources(), config.exported(), - config.allResources() + config.allResources(), + config.flow() ); } vm.doCompile(fixedNames.addAndNew(both)); @@ -419,6 +424,15 @@ return object + "." + mangledName; } + @Override + protected Flow checkFlow(byte[] byteCodes) { + if (flow == null) { + return null; + } + Flow f = new Flow(byteCodes); + return flow.analyze(f) ? f : null; + } + private final class ExportedMethodFinder implements ClassDataCache.TraversalCallback { private final ExportedSymbols exportedSymbols; @@ -452,9 +466,10 @@ private static final class Standalone extends VM { private Standalone(Appendable out, Bck2Brwsr.Resources resources, - StringArray explicitlyExported, StringArray asBinary + StringArray explicitlyExported, StringArray asBinary, + Flow.Analyzer flow ) { - super(out, resources, explicitlyExported, asBinary); + super(out, resources, explicitlyExported, asBinary, flow); } @Override @@ -700,9 +715,9 @@ private Extension(Appendable out, Bck2Brwsr.Resources resources, String[] extClassesArray, StringArray explicitlyExported, - StringArray asBinary, StringArray classpath + StringArray asBinary, StringArray classpath, Flow.Analyzer flow ) throws IOException { - super(out, resources, explicitlyExported, asBinary); + super(out, resources, explicitlyExported, asBinary, flow); this.extensionClasses = StringArray.asList(extClassesArray); this.classpath = classpath; }