From 1a5c9bf7a375c084ba4314c5adab7bced33feadb Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Mon, 26 Sep 2011 23:30:19 +0200 Subject: [PATCH 1/8] fixed possible NPE, as "m" can only be null at this position --- src/main/java/erjang/ERT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/erjang/ERT.java b/src/main/java/erjang/ERT.java index 37d1d580..9799413b 100644 --- a/src/main/java/erjang/ERT.java +++ b/src/main/java/erjang/ERT.java @@ -626,7 +626,7 @@ public static EFun resolve_fun(EObject mod, EObject fun, int arity) { final EFun pfun = EModuleManager.resolve(new FunID(pmod, f, arity+1)); - return EFun.get_fun_with_handler(m.toString(), f.toString(), arity, new EFunHandler() { + return EFun.get_fun_with_handler(pmod.toString(), f.toString(), arity, new EFunHandler() { @Override public EObject invoke(EProc proc, EObject[] args) throws Pausable { EObject[] real_args = new EObject[args.length+1]; From 5b6464374bf2270f16aa5ad91d063c3b6385e0eb Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Thu, 15 Dec 2011 00:28:53 +0100 Subject: [PATCH 2/8] ignoring some more files --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1b836dd0..caff92b2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ classes target *.beam src/main/java/erjang/beam/interpreter/Interpreter.java -src/test/java/erjang/*_TEST.java \ No newline at end of file +src/test/java/erjang/*_TEST.java +otp-*.jar +erjang.log From f437f8f9a6689557c4c2b881fd738f8ec6d5f716 Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Thu, 12 Apr 2012 23:31:54 +0200 Subject: [PATCH 3/8] added EObjectIterator and made ECons Iterable --- src/main/java/erjang/ECons.java | 7 +- src/main/java/erjang/EObjectIterator.java | 58 ++++++ src/test/java/erjang/EObjectIteratorTest.java | 190 ++++++++++++++++++ 3 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/main/java/erjang/EObjectIterator.java create mode 100644 src/test/java/erjang/EObjectIteratorTest.java diff --git a/src/main/java/erjang/ECons.java b/src/main/java/erjang/ECons.java index 8e1e303e..debb0f57 100644 --- a/src/main/java/erjang/ECons.java +++ b/src/main/java/erjang/ECons.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.io.IOException; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -28,7 +29,7 @@ import erjang.m.ets.EPattern; import erjang.m.ets.ETermPattern; -public abstract class ECons extends EObject { +public abstract class ECons extends EObject implements Iterable { @Override int cmp_order() { @@ -241,4 +242,8 @@ public void collectCharList(CharCollector out) } else tail.collectCharList(out); } + @Override + public Iterator iterator() { + return new EObjectIterator(this); + } } diff --git a/src/main/java/erjang/EObjectIterator.java b/src/main/java/erjang/EObjectIterator.java new file mode 100644 index 00000000..517809c5 --- /dev/null +++ b/src/main/java/erjang/EObjectIterator.java @@ -0,0 +1,58 @@ +package erjang; + +import java.util.Iterator; +import java.util.NoSuchElementException; + + +public class EObjectIterator implements Iterator { + private EObject object = null; + + public EObjectIterator(EObject object) { + this.object = object; + } + + /* (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + public boolean hasNext() { + if (object == null) { + return false; + } + return !object.isNil(); + } + + /* (non-Javadoc) + * @see java.util.Iterator#next() + */ + public EObject next() { + if ((object == null) + || object.isNil()) { + throw new NoSuchElementException(); + } + + EObject next; + + ECons cons = object.testCons(); + if ((cons != null) + // TODO how to handle EString? return each character? Or return it as single element only? + // for now we don't treat EString as collection, but as single element + && !(cons instanceof EString)) { + next = cons.head(); + object = cons.tail(); + } + else { + // simple object, return it and set next object to null + next = object; + object = null; + } + return next; + } + + /* (non-Javadoc) + * @see java.util.Iterator#remove() + */ + public void remove() { + // not supported + throw new UnsupportedOperationException(); + } +} diff --git a/src/test/java/erjang/EObjectIteratorTest.java b/src/test/java/erjang/EObjectIteratorTest.java new file mode 100644 index 00000000..4925a97a --- /dev/null +++ b/src/test/java/erjang/EObjectIteratorTest.java @@ -0,0 +1,190 @@ +package erjang; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import junit.framework.TestCase; + +public class EObjectIteratorTest extends TestCase { + public void testNil() throws Exception { + Iterator it = new EObjectIterator(ERT.NIL); + assertNotNull(it); + assertFalse(it.hasNext()); + } + + public void testNull() throws Exception { + Iterator it = new EObjectIterator(null); + assertNotNull(it); + assertFalse(it.hasNext()); + } + + public void testEString() throws Exception { + EString message = EString.fromString("Hello, World!"); + Iterator it = new EObjectIterator(message); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertEquals(message, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } + + public void testEInt() throws Exception { + EInteger num = ESmall.make(42); + Iterator it = new EObjectIterator(num); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertEquals(num, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } + + public void testSeqWithSingleNil() throws Exception { + ESeq seq = ESeq.fromArray(new EObject[] { + ERT.NIL + }); + assertEquals(1, seq.length()); + Iterator it = seq.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertTrue(o.isNil()); + assertEquals(ERT.NIL, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } + + public void testSeqWithTwoNils() throws Exception { + ESeq seq = ESeq.fromArray(new EObject[] { + ERT.NIL, + ERT.NIL + }); + assertEquals(2, seq.length()); + Iterator it = seq.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertTrue(o.isNil()); + assertEquals(ERT.NIL, o); + assertTrue(it.hasNext()); + o = it.next(); + assertNotNull(o); + assertTrue(o.isNil()); + assertEquals(ERT.NIL, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } + + public void testSeqWithSingleObject() throws Exception { + EObject first = EString.fromString("Hello"); + ESeq seq = ESeq.fromArray(new EObject[] { + first + }); + assertEquals(1, seq.length()); + Iterator it = seq.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertEquals(first, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } + + public void testSeqWithTwoObjects() throws Exception { + EObject first = EString.fromString("Hello"); + EObject second = EString.fromString("World"); + ESeq seq = ESeq.fromArray(new EObject[] { + first, + second + }); + assertEquals(2, seq.length()); + Iterator it = seq.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertEquals(first, o); + assertTrue(it.hasNext()); + o = it.next(); + assertNotNull(o); + assertEquals(second, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } + + public void testSeqWithThreeObjects() throws Exception { + EObject first = EString.fromString("Hello"); + EObject second = EString.fromString("World"); + EObject third = ESmall.make(42); + ESeq seq = ESeq.fromArray(new EObject[] { + first, + second, + third + }); + assertEquals(3, seq.length()); + Iterator it = seq.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + EObject o = it.next(); + assertNotNull(o); + assertEquals(first, o); + assertTrue(it.hasNext()); + o = it.next(); + assertNotNull(o); + assertEquals(second, o); + assertTrue(it.hasNext()); + o = it.next(); + assertNotNull(o); + assertEquals(third, o); + assertFalse(it.hasNext()); + try { + it.next(); + fail("no more elements, iterator should throw exception"); + } + catch (NoSuchElementException t) { + // expected + } + } +} From 1abbec5fb60ad26779f9e3c118146da632989197 Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Sat, 14 Apr 2012 02:34:29 +0200 Subject: [PATCH 4/8] Improved startup and auto-detection of Erlang environment. Erlang env is searched for in: -root arg $PATH (searching for 'erl') ERTS and OTP versions are detected automatically. Also, loading Erlang environment from class path now works correctly --- ej | 8 - erjang_cfg.properties | 6 +- src/main/java/erjang/ERT.java | 14 + src/main/java/erjang/ErjangConfig.java | 4 +- src/main/java/erjang/Main.java | 196 ++--------- src/main/java/erjang/OTPMain.java | 60 ++-- src/main/java/erjang/RuntimeInfo.java | 330 ++++++++++++++++++ .../driver/efile/ClassPathResource.java | 29 +- src/main/java/erjang/driver/efile/EFile.java | 14 +- src/main/java/erjang/m/erlang/ErlProc.java | 23 +- 10 files changed, 455 insertions(+), 229 deletions(-) create mode 100644 src/main/java/erjang/RuntimeInfo.java diff --git a/ej b/ej index f70753eb..45058e10 100755 --- a/ej +++ b/ej @@ -7,23 +7,15 @@ EJ_CMD=`which "$0"` while LINK=`readlink "$EJ_CMD"`; do EJ_CMD=$LINK; done ERJANG_DIR=`dirname "$EJ_CMD"` -source "$ERJANG_DIR/env_cfg" - ## -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=n,suspend=n \ exec java \ -server \ -Xmx1g -Xss50m \ -XX:PermSize=128m \ - -Derjang.configfile="$PROP_FILE" \ - -Derjang.erts.version="$ERTS_VSN" \ - -Derjang.otp.version="$OTP_VSN" \ -jar $ERJANG_DIR/erjang-0.1.jar \ \ -progname ej \ - -home "$HOME" \ - -root "$ERL_ROOT" \ +A 10 \ +S 1 \ - +e "$ERTS_VSN" \ "$@" diff --git a/erjang_cfg.properties b/erjang_cfg.properties index 8b080c18..9d01174f 100644 --- a/erjang_cfg.properties +++ b/erjang_cfg.properties @@ -1,6 +1,6 @@ -erjang.otp.root = /usr/local/lib/erlang -erjang.erts.version = 5.8.3 -erjang.otp.version = R14B02 +#erjang.otp.root = /usr/local/lib/erlang +#erjang.erts.version = 5.8.3 +#erjang.otp.version = R14B02 # erjang.debug.port=true # erjang.debug.inet=true diff --git a/src/main/java/erjang/ERT.java b/src/main/java/erjang/ERT.java index 9799413b..b8a118c2 100644 --- a/src/main/java/erjang/ERT.java +++ b/src/main/java/erjang/ERT.java @@ -1141,4 +1141,18 @@ public void write(byte[] b, int off, int len) throws IOException { } } + /** + * contains versions, paths and other information. + * use {@link ERT#setRuntimeInfo(RuntimeInfo)} to set this field. + */ + public static RuntimeInfo runtime_info = null; + + public static void setRuntimeInfo(RuntimeInfo info) { + // TODO perform synchronization? We usually set this + // field only once from Main before ERT is started + runtime_info = info; + if (runtime_info != null) { + System.setProperty("erjang.path", runtime_info.erl_bootstrap_ebindir); + } + } } diff --git a/src/main/java/erjang/ErjangConfig.java b/src/main/java/erjang/ErjangConfig.java index f246010b..88c3f926 100644 --- a/src/main/java/erjang/ErjangConfig.java +++ b/src/main/java/erjang/ErjangConfig.java @@ -36,9 +36,9 @@ public class ErjangConfig { static { String configFileName = System.getProperty("erjang.configfile"); - if (configFileName != null) { + if (configFileName != null && configFileName.trim().length() > 0) { try { - FileInputStream in = new FileInputStream(configFileName); + FileInputStream in = new FileInputStream(configFileName.trim()); try { Properties properties = new Properties(); properties.load(in); diff --git a/src/main/java/erjang/Main.java b/src/main/java/erjang/Main.java index 49497f9e..8d076acb 100644 --- a/src/main/java/erjang/Main.java +++ b/src/main/java/erjang/Main.java @@ -18,171 +18,16 @@ package erjang; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; -import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import erjang.driver.efile.EFile; public class Main { public static final String SYSTEM_ARCHITECTURE = "java"; public static final String DRIVER_VERSION = "1.5"; - - // determine dynamically from $OTPROOT (and make therefore non-final), if unspecified - public static String otp_version = (ErjangConfig.hasString("erjang.otp.version") ? ErjangConfig.getString("erjang.otp.version", null) : null); - static String erts_version = (ErjangConfig.hasString("erjang.erts.version") ? "erts-"+ErjangConfig.getString("erjang.erts.version", null) : null); - static String erl_rootdir; - static String erl_bootstrap_ebindir; - - public static String erts_version() { - return erts_version; - } - - static String setup(String cmd_line_root) { - - erl_rootdir = cmd_line_root; - if (cmd_line_root == null) - erl_rootdir = System.getenv("OTPROOT"); - - if (erl_rootdir == null) { - erl_rootdir = guess_erl_root(); - } - - File erlangRoot = new File(erl_rootdir); - if (!erlangRoot.isDirectory()) { - return null; - } - - if (erts_version == null) { - // guess erts version from directory $OTPROOT/lib/erts- - File libDir = new File(erlangRoot, "lib"); - String[] ertsDirs = libDir.list(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.startsWith("erts-"); - } - }); - if (ertsDirs != null) { - for (int d = 0; d < ertsDirs.length; d++) { - String dir = ertsDirs[d]; - erts_version = dir; - } - } - } - - if (otp_version == null) { - // guess OTP version from directory $OTPROOT/bin/start.script - File startScript = new File(erlangRoot, "bin/start.script"); - otp_version = guess_otp_version(startScript); - } - - if (otp_version == null) { - // guess OTP version from directory $OTPROOT/bin/start.script - File startScript = new File(erlangRoot, "releases/RELEASES"); - otp_version = guess_otp_version(startScript); - } - - if (otp_version == null) { - ERT.log.severe("Cannot determine OTP version! Please specify system property 'erjang.otp.version'"); - return null; - } - - erl_bootstrap_ebindir = System.getenv("ERL_BOOTSTRAP_EBIN"); - if (erl_bootstrap_ebindir == null) { - erl_bootstrap_ebindir = erl_rootdir + File.separator + "lib" + File.separator - + erts_version + File.separator + "ebin"; - } - - return erl_rootdir; - } - - private static String guess_otp_version(File file) { - if ((file == null) || !file.exists() || !file.canRead()) { - return null; - } - - BufferedReader reader = null; - try { - Pattern pattern = Pattern.compile(".*\"(.*OTP[\\s]+APN.*)\",\"(R\\w+)\".*"); - reader = new BufferedReader(new FileReader(file)); - String line; - while ((line = reader.readLine()) != null) { - Matcher match = pattern.matcher(line); - if (!match.matches()) { - continue; - } - String otpVersion = match.group(2); - if ((otpVersion != null) - && (otpVersion.length() > 0)) { - return otpVersion; - } - } - - } - catch (Throwable t) { - // ignore - } - finally { - // close reader - if (reader != null) { - try { - reader.close(); - } - catch (Throwable t) { - // ignore - } - } - } - - return null; - } - private static String guess_erl_root() { - - if (Main.class.getClassLoader().getResource("bin/start.boot") != null) { - return EFile.RESOURCE_PREFIX.substring(0, EFile.RESOURCE_PREFIX.length()-1); - } - - // this logic works on Unixes ... what about windows? - String path = System.getenv("PATH"); - List paths = new ArrayList(); - paths.addAll(Arrays.asList(path.split(File.pathSeparator))); - // also check canonical locations /usr/local/lib/erlang and - // /opt/local/lib/erlang - paths.add("/usr/local/lib/erlang/bin"); - paths.add("/opt/local/lib/erlang/bin"); - for (String elem : paths) { - File dir = new File(elem); - // TODO check for some other file, which might not be s - // symbolic link in a generic bin dir such as /usr/local/bin - File erl = new File(dir, "erl"); - if (erl.exists()) { - - if (dir.getParent() == null) - continue; - - File lib = new File (dir.getParent(), "lib"); - File root = new File(lib, "erlang"); - - if (root.exists()) { - return root.getAbsolutePath(); - } - - } - } - - ERT.log.severe("Cannot find OTPROOT directory\n" - + "Pass -root , or set environment variable."); - - return null; - } /** * @param args @@ -197,6 +42,8 @@ public static void main(String[] args) throws Exception { ArrayList ra = new ArrayList(); String cmd_line_root = null; + String otp_version = (ErjangConfig.hasString("erjang.otp.version") ? ErjangConfig.getString("erjang.otp.version", null) : null); + String erts_version = (ErjangConfig.hasString("erjang.erts.version") ? "erts-"+ErjangConfig.getString("erjang.erts.version", null) : null); for (int i = 0; i < args.length; i++) { if ("-root".equals(args[i]) && i < args.length) { cmd_line_root = args[i+1]; @@ -207,13 +54,22 @@ public static void main(String[] args) throws Exception { } } - String root = setup(cmd_line_root); - - if (cmd_line_root == null) { - ra.add("-root"); - ra.add(root); + RuntimeInfo runtimeInfo = RuntimeInfo.setup(erts_version, otp_version, cmd_line_root); + try { + // verify we have a working configuration + runtimeInfo.verify(); + } + catch (RuntimeException e) { + String reason = e.getMessage(); + ERT.log.severe(reason); + System.err.println(reason); + return; } + String root = runtimeInfo.erl_rootdir; + ra.add("-root"); + ra.add(root); + arg_loop: for (int i = 0; i < args.length; i++) { String arg = args[i]; @@ -225,6 +81,12 @@ public static void main(String[] args) throws Exception { break; } + if ("-root".equals(args[i]) && i < args.length) { + // skip "-root " arg, was set above + i++; + continue; + } + if (arg.startsWith("+")) { switch (arg.charAt(1)) { case 'a': @@ -253,14 +115,18 @@ public static void main(String[] args) throws Exception { ra.add(arg); } - System.setProperty("erjang.path", erl_bootstrap_ebindir); + if (!ra.contains("-home")) { + ra.add("-home"); + ra.add(System.getProperty("user.home")); + } + + ERT.setRuntimeInfo(runtimeInfo); - if (!(new File(erl_bootstrap_ebindir)).exists() && !erl_bootstrap_ebindir.startsWith(EFile.RESOURCE_PREFIX)) { - ERT.log.severe("No bootstrap classes at: "+erl_bootstrap_ebindir); - throw new IllegalArgumentException("No bootstrap classes at: "+erl_bootstrap_ebindir); + if (!(new File(runtimeInfo.erl_bootstrap_ebindir)).exists() && !runtimeInfo.erl_bootstrap_ebindir.startsWith(EFile.RESOURCE_PREFIX)) { + ERT.log.severe("No bootstrap classes at: "+runtimeInfo.erl_bootstrap_ebindir); + throw new IllegalArgumentException("No bootstrap classes at: "+runtimeInfo.erl_bootstrap_ebindir); } OTPMain.main(ra.toArray(new String[ra.size()])); } - } diff --git a/src/main/java/erjang/OTPMain.java b/src/main/java/erjang/OTPMain.java index 36accfb7..d311978c 100644 --- a/src/main/java/erjang/OTPMain.java +++ b/src/main/java/erjang/OTPMain.java @@ -21,6 +21,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.logging.FileHandler; import java.util.logging.Handler; @@ -50,31 +51,33 @@ public class OTPMain { new erjang.driver.js.EJSDriver() }; - public static void load_modules_and_drivers() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { - - if (Main.erl_bootstrap_ebindir.startsWith(EFile.RESOURCE_PREFIX)) { - - for (String m : MODULES) { - String beam_path = Main.erl_bootstrap_ebindir + "/" + m + ".beam"; - EBinary bin = ClassPathResource.read_file(beam_path); - if (bin == null) { - throw new FileNotFoundException(beam_path); + public static void load_modules_and_drivers(List modules, List drivers) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { + + if (modules != null) { + String erl_bootstrap_ebindir = ERT.runtime_info.erl_bootstrap_ebindir; + if (ClassPathResource.isResource(erl_bootstrap_ebindir)) { + + for (String m : modules) { + String beam_path = erl_bootstrap_ebindir + "/" + m + ".beam"; + EBinary bin = ClassPathResource.read_file(beam_path); + if (bin == null) { + throw new FileNotFoundException(beam_path); + } + EModuleLoader.load_module(m, bin); } - EModuleLoader.load_module(m, bin); - } - - } else { - for (String m : MODULES) { - ERT.load_module(EAtom.intern(m)); - } + + } else { + for (String m : modules) { + ERT.load_module(EAtom.intern(m)); + } + } } - for (EDriver d : DRIVERS) { - erjang.driver.Drivers.register(d); - } - for (EDriver d : extra_drivers) { - erjang.driver.Drivers.register(d); - } + if (drivers != null) { + for (EDriver d : drivers) { + erjang.driver.Drivers.register(d); + } + } } public static void start_otp_ring0(ESeq argv) { @@ -111,6 +114,14 @@ protected static ESeq process_args(String[] args) { return argv; } + /** + * {@link ERT#setRuntimeInfo(RuntimeInfo)} should have been called, before calling this method + * @param args + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws IOException + */ public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { ESeq argv = process_args(args); @@ -119,10 +130,11 @@ public static void main(String[] args) throws ClassNotFoundException, Instantiat // Logger.getLogger("erjang").setLevel(Level.FINE); // Logger.getLogger("kilim.Task").setLevel(Level.FINEST); - load_modules_and_drivers(); + load_modules_and_drivers(Arrays.asList(MODULES), Arrays.asList(DRIVERS)); + load_modules_and_drivers(null, extra_drivers); start_otp_ring0(argv); - System.out.println("done."); + ERT.getOutputStream().println("done."); } static List extra_drivers = new ArrayList(); diff --git a/src/main/java/erjang/RuntimeInfo.java b/src/main/java/erjang/RuntimeInfo.java new file mode 100644 index 00000000..484f817c --- /dev/null +++ b/src/main/java/erjang/RuntimeInfo.java @@ -0,0 +1,330 @@ +/** + * This file is part of Erjang - A JVM-based Erlang VM + * + * Copyright (c) 2010 by Wolfgang Schell + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +package erjang; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileReader; +import java.io.FilenameFilter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import erjang.driver.efile.ClassPathResource; +import erjang.driver.efile.EFile; + +/** + * Holder of runtime information such as Erlang and OTP version, + * root path, etc. + * + * @author jetztgradnet + */ +public class RuntimeInfo { + public final String erts_version; + public final String otp_version; + public final String erl_rootdir; + public final String erl_bootstrap_ebindir; + + /** + * the new unicode driver interface is used since OTP version R14B01. + * + * @see http://www.erlang.org/doc/apps/stdlib/unicode_usage.html#id60205 + */ + public final boolean unicodeDriverInterface; + + public RuntimeInfo(String erts_version, String otp_version, String erl_rootdir, String erl_bootstrap_ebindir) { + this.erts_version = erts_version; + this.otp_version = otp_version; + this.erl_rootdir = erl_rootdir; + this.erl_bootstrap_ebindir = erl_bootstrap_ebindir; + + if (otp_version != null) { + unicodeDriverInterface = ("R14B".compareTo(otp_version) < 0); + } + else { + unicodeDriverInterface = false; + } + } + + public void verify() throws RuntimeException { + if (isNullOrEmpty(erl_rootdir)) { + throw new RuntimeException("Erlang runtime not found, please specify root directory using parameter '-root '"); + } + + if (isNullOrEmpty(erts_version)) { + throw new RuntimeException("Cannot determine ERTS version! Please specify system property 'erjang.erts.version'"); + } + + if (isNullOrEmpty(otp_version)) { + throw new RuntimeException("Cannot determine OTP version! Please specify system property 'erjang.otp.version'"); + } + } + + public boolean isClassPathInstallation() { + return isClassPathInstallation(erl_rootdir); + } + + public static boolean isClassPathInstallation(String erl_rootdir) { + return ClassPathResource.isResource(erl_rootdir); + } + + protected static final String ERLANG_RELEASESTARTFILE = "releases/start_erl.data"; + protected static final String ERLANG_RELEASEFILE = "releases/RELEASES"; + protected static final String ERLANG_STARTSCRIPT = "bin/start.script"; + protected static final String ERLANG_BOOTFILE = "bin/start.boot"; + protected static final String ERLANG_STARTSCRIPT_REGEX = ".*\".*OTP[\\s]+APN.*\",\"(R\\w+)\".*"; + protected static final String ERLANG_RELEASEFILE_REGEX = ".*\".*OTP[\\s]+APN.*\",\"(R\\w+)\",\"([0-9]\\.[0-9]\\.[0-9])\".*"; + protected static final String ERLANG_RELEASESTARTFILE_REGEX = "(\\S+)\\s+(\\S+)"; + + // determine dynamically from $OTPROOT (and make therefore non-final), if unspecified + + public static RuntimeInfo setup(String erts_version, String otp_version, String erl_rootdir) { + if (isNullOrEmpty(erl_rootdir)) { + erl_rootdir = guess_erl_root(); + } + if (!isNullOrEmpty(erl_rootdir)) { + if (erl_rootdir.endsWith("/") || erl_rootdir.endsWith("\\")) { + erl_rootdir = erl_rootdir.substring(0, erl_rootdir.length() - 1); + } + } + + if (isNullOrEmpty(erts_version)) { + erts_version = guess_erts_version(erl_rootdir); + } + + if (isNullOrEmpty(otp_version)) { + otp_version = guess_otp_version(erl_rootdir); + } + + String erl_bootstrap_ebindir = System.getenv("ERL_BOOTSTRAP_EBIN"); + if (isNullOrEmpty(erl_bootstrap_ebindir)) { + erl_bootstrap_ebindir = erl_rootdir; + if (!erl_bootstrap_ebindir.endsWith("/") && !erl_bootstrap_ebindir.endsWith("\\")) { + erl_bootstrap_ebindir += File.separator; + } + erl_bootstrap_ebindir += "lib" + File.separator + "erts-" + erts_version + File.separator + "ebin"; + } + + return new RuntimeInfo(erts_version, otp_version, erl_rootdir, erl_bootstrap_ebindir); + } + + public static String guess_otp_version(String erl_rootdir) { + // guess OTP version from directory $OTPROOT/releases/RELEASES + String otp_version = guess_version(ERLANG_RELEASESTARTFILE_REGEX, 2, erl_rootdir, ERLANG_RELEASESTARTFILE); + if (isNullOrEmpty(otp_version)) { + otp_version = guess_version(ERLANG_RELEASEFILE_REGEX, 1, erl_rootdir, ERLANG_RELEASEFILE); + } + if (isNullOrEmpty(otp_version)) { + // fallback: guess OTP version from directory $OTPROOT/bin/start.script + otp_version = guess_version(ERLANG_STARTSCRIPT_REGEX, 1, erl_rootdir, ERLANG_STARTSCRIPT); + } + + return otp_version; + } + + public static String guess_erts_version(String erl_rootdir) { + // guess ERTS version from directory $OTPROOT/releases/RELEASES + String erts_version = guess_version(ERLANG_RELEASESTARTFILE_REGEX, 1, erl_rootdir, ERLANG_RELEASESTARTFILE); + if (isNullOrEmpty(erts_version)) { + erts_version = guess_version(ERLANG_RELEASEFILE_REGEX, 2, erl_rootdir, ERLANG_RELEASEFILE); + } + if (isNullOrEmpty(erts_version)) { + // fallback: guess ERTS version from erts directory + File erlangRoot = new File(erl_rootdir); + if (erlangRoot.isDirectory()) { + // guess erts version from directory $OTPROOT/lib/erts- + File libDir = new File(erlangRoot, "lib"); + String[] ertsDirs = libDir.list(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.startsWith("erts-"); + } + }); + if (ertsDirs != null) { + for (int d = 0; d < ertsDirs.length; d++) { + String dir = ertsDirs[d]; + erts_version = dir; + } + } + } + } + return erts_version; + } + + protected static String guess_version(String regex, int group, String erl_rootdir, String fileName) { + if (isNullOrEmpty(erl_rootdir) || RuntimeInfo.isClassPathInstallation(erl_rootdir)) { + return guess_version_from_resource(regex, group, fileName); + } + File erlangRoot = new File(erl_rootdir); + if (erlangRoot.isDirectory()) { + return guess_version_from_file(regex, group, new File(erlangRoot, fileName)); + } + else { + return guess_version_from_resource(regex, group, fileName); + } + } + + protected static String guess_version_from_resource(String regex, int group, String fileName) { + InputStream input = null; + + try { + input = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); + return guess_version_from_reader(regex, group, new InputStreamReader(input)); + } + catch (Throwable t) { + // ignore + } + finally { + // close reader + closeQuietly(input); + } + + return null; + } + + protected static String guess_version_from_file(String regex, int group, File file) { + if ((file == null) || !file.exists() || !file.canRead()) { + return null; + } + + FileReader reader = null; + try { + reader = new FileReader(file); + return guess_version_from_reader(regex, group, reader); + } + catch (Throwable t) { + // ignore + } + finally { + // close reader + closeQuietly(reader); + } + + return null; + } + + protected static String guess_version_from_reader(String regex, int group, Reader input) { + BufferedReader reader = null; + try { + Pattern pattern = Pattern.compile(regex); + reader = new BufferedReader(input); + String line; + while ((line = reader.readLine()) != null) { + Matcher match = pattern.matcher(line); + if (!match.matches()) { + continue; + } + String versionString = match.group(group); + if ((versionString != null) + && (versionString.length() > 0)) { + return versionString.trim(); + } + } + } + catch (Throwable t) { + // ignore + } + finally { + // close reader + closeQuietly(reader); + } + + return null; + } + + protected static boolean isNullOrEmpty(String text) { + if (text == null || text.trim().length() == 0) { + return true; + } + return false; + } + + public static String guess_erl_root() { + + String erl_rootdir = System.getenv("OTPROOT"); + + if (isNullOrEmpty(erl_rootdir)) { + // check whether we have Erlang/OTP on the classpath + if (Thread.currentThread().getContextClassLoader().getResource(ERLANG_BOOTFILE) != null) { + erl_rootdir = EFile.RESOURCE_PREFIX; + } + } + + if (isNullOrEmpty(erl_rootdir)) { + // discover Erlang from $PATH + // this logic works on Unixes ... what about windows? + String path = System.getenv("PATH"); + List paths = new ArrayList(); + paths.addAll(Arrays.asList(path.split(File.pathSeparator))); + // also check canonical locations /usr/local/lib/erlang and + // /opt/local/lib/erlang + paths.add("/usr/local/lib/erlang/bin"); + paths.add("/opt/local/lib/erlang/bin"); + for (String elem : paths) { + File dir = new File(elem); + // TODO check for some other file, which might not be a + // symbolic link in a generic bin dir such as /usr/local/bin + File erl = new File(dir, "erl"); + if (erl.exists()) { + File baseDir = dir.getParentFile(); + if (baseDir == null) + continue; + + // check for /releases + File releases = new File(baseDir, "releases"); + if (releases.isDirectory()) { + erl_rootdir = baseDir.getAbsolutePath(); + break; + } + + + // check for /lib/erlang + // (Unix-style installation with Erlang bin living next to lib/erlang) + File lib = new File (baseDir, "lib"); + File root = new File(lib, "erlang"); + + if (root.exists()) { + erl_rootdir = root.getAbsolutePath(); + break; + } + } + } + } + + return erl_rootdir; + } + + protected static void closeQuietly(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } + catch (Throwable t) { + // ignore + } + } + } + +} diff --git a/src/main/java/erjang/driver/efile/ClassPathResource.java b/src/main/java/erjang/driver/efile/ClassPathResource.java index 617971d5..22a94d7f 100644 --- a/src/main/java/erjang/driver/efile/ClassPathResource.java +++ b/src/main/java/erjang/driver/efile/ClassPathResource.java @@ -19,15 +19,34 @@ import erjang.driver.IO; public class ClassPathResource { + public static boolean isResource(String fileName) { + return fileName.startsWith(EFile.RESOURCE_PREFIX); + } + public static String getResourceName(String fileName) { + if (!isResource(fileName)) + return fileName; + + fileName = fileName.substring(EFile.RESOURCE_PREFIX.length()); + if (fileName.startsWith(File.separator) || fileName.startsWith("/")) { + fileName = fileName.substring(1); + } + + return fileName; + } public static EBinary read_file(String name) { if (!name.startsWith(EFile.RESOURCE_PREFIX)) return null; - + + String fileName = getResourceName(name); InputStream resource = ClassPathResource.class.getClassLoader() - .getResourceAsStream( - name.substring(EFile.RESOURCE_PREFIX.length())); + .getResourceAsStream(fileName); + if (resource == null) { + // fallback: check context class loader + resource = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(fileName); + } if (resource == null) { return null; @@ -196,8 +215,8 @@ private static void put_time(ByteBuffer res, long time) { } static ZipEntry get_entry(String path) throws IOException { - Enumeration out = ClassPathResource.class.getClassLoader() - .getResources(path.substring(EFile.RESOURCE_PREFIX.length())); + String fileName = getResourceName(path); + Enumeration out = ClassPathResource.class.getClassLoader().getResources(fileName); while (out.hasMoreElements()) { URL u = out.nextElement(); diff --git a/src/main/java/erjang/driver/efile/EFile.java b/src/main/java/erjang/driver/efile/EFile.java index 9c15f6c1..8567bd2d 100644 --- a/src/main/java/erjang/driver/efile/EFile.java +++ b/src/main/java/erjang/driver/efile/EFile.java @@ -795,8 +795,7 @@ public void ready() throws Pausable { return; } - if (name.startsWith(RESOURCE_PREFIX)) { - + if (ClassPathResource.isResource(name)) { EBinary data = ClassPathResource.read_file(name); if (data == null) { reply_posix_error(Posix.ENOENT); @@ -1516,7 +1515,7 @@ public void ready() throws Pausable { final String file_name = IO.strcpy(buf); final File file = ERT.newFile(file_name); - if (file_name.startsWith(RESOURCE_PREFIX)) { + if (ClassPathResource.isResource(file_name)) { ClassPathResource.fstat(this, file_name); return; } @@ -1960,13 +1959,6 @@ void reply_list_directory(String[] files) throws Pausable { driver_output2(resbuf, null); } - /** - * the new unicode driver interface is used since OTP version R14B01. - * - * @see http://www.erlang.org/doc/apps/stdlib/unicode_usage.html#id60205 - */ - static boolean unicodeDriverInterface = ("R14B".compareTo(erjang.Main.otp_version == null ? "" : erjang.Main.otp_version) < 0); - /** * Determine whether to use the new unicode driver interface * from R14B01. @@ -1978,6 +1970,6 @@ void reply_list_directory(String[] files) throws Pausable { * @see http://www.erlang.org/doc/apps/stdlib/unicode_usage.html#id60205 */ private static boolean isUnicodeDriverInterface() { - return unicodeDriverInterface; + return ERT.runtime_info.unicodeDriverInterface; } } diff --git a/src/main/java/erjang/m/erlang/ErlProc.java b/src/main/java/erjang/m/erlang/ErlProc.java index 82dd28fe..a6e88169 100644 --- a/src/main/java/erjang/m/erlang/ErlProc.java +++ b/src/main/java/erjang/m/erlang/ErlProc.java @@ -34,7 +34,6 @@ import erjang.ECons; import erjang.EFun; import erjang.EHandle; -import erjang.EInternalPID; import erjang.EModuleManager; import erjang.EObject; import erjang.EPID; @@ -57,7 +56,6 @@ import erjang.Import; import erjang.Main; import erjang.NotImplemented; -import erjang.OTPMain; /** * @@ -300,7 +298,7 @@ public static EObject halt(EProc proc, EObject value) { } ERT.shutdown(); - throw new erjang.ErlangHalt(); + throw new ErlangHalt(); } @BIF @@ -412,10 +410,10 @@ static public EObject demonitor(EProc self, EObject ref) throws Pausable { */ @BIF static public EObject demonitor(EProc self, EObject ref, EObject options) throws Pausable { - return demonitor((ETask)self, ref, options); + return demonitor((ETask)self, ref, options); } - static public EObject demonitor(ETask self, EObject ref, EObject options) throws Pausable { + static public EObject demonitor(ETask self, EObject ref, EObject options) throws Pausable { ERef r = ref.testReference(); ESeq o = options.testSeq(); @@ -582,7 +580,6 @@ static EObject system_info(EProc proc, EObject type) { return ERT.TRUE; } else if (type == am_thread_pool_size) { - // TODO: hook up to thread pool return new ESmall(ERT.threadPoolSize()); @@ -598,12 +595,16 @@ static EObject system_info(EProc proc, EObject type) { return ERT.TRUE; } else if (type == am_version) { - return EString.fromString( erjang.Main.erts_version().substring("erts-".length()) ); + String erts_version = ERT.runtime_info.erts_version; + // remove prefix + String prefix = "erts-"; + if (erts_version.startsWith(prefix)) { + erts_version = erts_version.substring(prefix.length()); + } + return EString.fromString(erts_version); } else if (type == am_otp_release) { - // TODO: be smarter somehow - return new EString(Main.otp_version); - + return new EString(ERT.runtime_info.otp_version); } else if (type == am_logical_processors) { // TODO: be smarter somehow return ERT.box(Runtime.getRuntime().availableProcessors()); @@ -631,7 +632,7 @@ static EObject system_info(EProc proc, EObject type) { return am_undefined; } else if (type == am_system_version) { - return new EString("Erjang ["+ erjang.Main.erts_version()+"]"); + return new EString("Erjang ["+ ERT.runtime_info.erts_version+"]"); } else if ((tup=ETuple2.cast(type)) != null) { From ea7163a49e5a20b25fa554b8feb8575089307994 Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Sat, 14 Apr 2012 02:37:52 +0200 Subject: [PATCH 5/8] calling Kilim weaver in Eclipse build, so weaving is also done from within Eclipse --- .externalToolBuilders/Kilim Weaver.launch | 17 ++++++++++ .project | 14 ++++---- weave.xml | 40 +++++++++++++++++++++++ 3 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 .externalToolBuilders/Kilim Weaver.launch create mode 100755 weave.xml diff --git a/.externalToolBuilders/Kilim Weaver.launch b/.externalToolBuilders/Kilim Weaver.launch new file mode 100644 index 00000000..2c4edabb --- /dev/null +++ b/.externalToolBuilders/Kilim Weaver.launch @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/.project b/.project index 9313b7bc..4087650e 100644 --- a/.project +++ b/.project @@ -6,18 +6,18 @@ - org.eclipse.dltk.core.scriptbuilder - - - - - org.erjang.kilim_builder.KilimBuilder + org.eclipse.jdt.core.javabuilder - org.eclipse.jdt.core.javabuilder + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + LaunchConfigHandle + <project>/.externalToolBuilders/Kilim Weaver.launch + diff --git a/weave.xml b/weave.xml new file mode 100755 index 00000000..650f99ce --- /dev/null +++ b/weave.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7cdb42c18d81fa1b3edd1698dc3f7b6124fb2b96 Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Sat, 14 Apr 2012 02:38:25 +0200 Subject: [PATCH 6/8] ignoring more files --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index caff92b2..52be439a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ target src/main/java/erjang/beam/interpreter/Interpreter.java src/test/java/erjang/*_TEST.java otp-*.jar -erjang.log +erjang.log* +*.launch From bf849467d49cfe6513137c8072e20ed5391ab5b7 Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Sat, 14 Apr 2012 02:47:31 +0200 Subject: [PATCH 7/8] bumped version to 0.2 --- build.xml | 2 +- ej | 2 +- ejc | 2 +- erl.cmd | 2 +- fib_test.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.xml b/build.xml index 4412a201..f729bfa9 100755 --- a/build.xml +++ b/build.xml @@ -1,6 +1,6 @@ - + diff --git a/ej b/ej index 45058e10..039ba66e 100755 --- a/ej +++ b/ej @@ -13,7 +13,7 @@ exec java \ -server \ -Xmx1g -Xss50m \ -XX:PermSize=128m \ - -jar $ERJANG_DIR/erjang-0.1.jar \ + -jar $ERJANG_DIR/erjang-0.2.jar \ \ -progname ej \ +A 10 \ diff --git a/ejc b/ejc index ead5c52c..515269ea 100755 --- a/ejc +++ b/ejc @@ -27,7 +27,7 @@ exec java \ -Derjang.configfile="$PROP_FILE" \ -Derjang.erts.version="$ERTS_VSN" \ -Derjang.otp.version="$OTP_VSN" \ - -cp "$ERJANG_DIR/erjang-0.1.jar" erjang.console.ERLConsole \ + -cp "$ERJANG_DIR/erjang-0.2.jar" erjang.console.ERLConsole \ \ -root "$ERL_ROOT" \ +A 10 \ diff --git a/erl.cmd b/erl.cmd index d3660999..31551429 100644 --- a/erl.cmd +++ b/erl.cmd @@ -2,7 +2,7 @@ IF "x%OTPROOT%" == "x" SET OTPROOT="C:/Program Files/erl5.7.5" -java -cp erjang-0.1.jar ^ +java -cp erjang-0.2.jar ^ erjang.Main ^ -root %OTPROOT% ^ %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/fib_test.sh b/fib_test.sh index f6c5085c..ad343070 100755 --- a/fib_test.sh +++ b/fib_test.sh @@ -1,3 +1,3 @@ #!/bin/sh -java -server -cp erjang-0.1.jar -Derjpath=./src/main/erl/preloaded/ebin:src/main/erl erjang.Erj fib:main +java -server -cp erjang-0.2.jar -Derjpath=./src/main/erl/preloaded/ebin:src/main/erl erjang.Erj fib:main From 14888f355e398b333cfec3b6f450605bc7dd8882 Mon Sep 17 00:00:00 2001 From: Wolfgang Schell Date: Sat, 14 Apr 2012 02:56:07 +0200 Subject: [PATCH 8/8] cleaned up ejc starter --- ejc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ejc b/ejc index 515269ea..0470421c 100755 --- a/ejc +++ b/ejc @@ -7,8 +7,6 @@ EJC_CMD=`which "$0"` while LINK=`readlink "$EJC_CMD"`; do EJC_CMD=$LINK; done ERJANG_DIR=`dirname "$EJC_CMD"` -source "$ERJANG_DIR/env_cfg" - ########################################################### # OS X - specific config if [[ $(uname) == "Darwin" ]]; then @@ -24,14 +22,8 @@ exec java \ -Xmx2g \ $OSX_DOCK_ICON \ $OSX_DOCK_NAME \ - -Derjang.configfile="$PROP_FILE" \ - -Derjang.erts.version="$ERTS_VSN" \ - -Derjang.otp.version="$OTP_VSN" \ -cp "$ERJANG_DIR/erjang-0.2.jar" erjang.console.ERLConsole \ \ - -root "$ERL_ROOT" \ +A 10 \ +S 1 \ - +e "$ERTS_VSN" \ - -home "$HOME" \ "$@"