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/.gitignore b/.gitignore
index 1b836dd0..52be439a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,7 @@ 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*
+*.launch
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/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 f70753eb..039ba66e 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 \
+ -jar $ERJANG_DIR/erjang-0.2.jar \
\
-progname ej \
- -home "$HOME" \
- -root "$ERL_ROOT" \
+A 10 \
+S 1 \
- +e "$ERTS_VSN" \
"$@"
diff --git a/ejc b/ejc
index ead5c52c..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.1.jar" erjang.console.ERLConsole \
+ -cp "$ERJANG_DIR/erjang-0.2.jar" erjang.console.ERLConsole \
\
- -root "$ERL_ROOT" \
+A 10 \
+S 1 \
- +e "$ERTS_VSN" \
- -home "$HOME" \
"$@"
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/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
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/main/java/erjang/ERT.java b/src/main/java/erjang/ERT.java
index 37d1d580..b8a118c2 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];
@@ -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) {
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
+ }
+ }
+}
diff --git a/weave.xml b/weave.xml
new file mode 100755
index 00000000..650f99ce
--- /dev/null
+++ b/weave.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+