/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* 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.beam;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.objectweb.asm.Type;
import erjang.EAtom;
import erjang.EBig;
import erjang.EDouble;
import erjang.ENative;
import erjang.EObject;
import erjang.EProc;
import erjang.ERT;
import erjang.ESmall;
import erjang.m.erlang.BinOps;
import erjang.m.erlang.ErlBif;
/**
* Used by the compiler to find and mange BIF definitions.
*
* To add bifs, add the classes to the first "static" block below.
*
* @author krab
*/
public class BIFUtil {
public static final Type EOBJECT_TYPE = Type.getType(EObject.class);
public static final Type EPROC_TYPE = Type.getType(EProc.class);
static Map<String, BIFHandler> bifs = new HashMap<String, BIFHandler>();
static Map<String, BIFHandler> guard_bifs = new HashMap<String, BIFHandler>();
static {
try {
loadBIFs(new String[]{"erlang", "error_logger", "ets", "lists", "math",
"net_kernel", "os", "re", "unicode",
"io_lib", "crypto", "file", "binary" });
} catch (Exception e) {
throw new Error("Missing native module", e);
}
}
static class Args {
Class<?>[] args;
private Args generic;
private int code;
Args(Type[] types) {
args = new Class[types.length];
for (int i = 0; i < types.length; i++) {
if (types[i] == Type.DOUBLE_TYPE) {
args[i] = double.class;
continue;
}
if (types[i] == Type.INT_TYPE) {
args[i] = int.class;
continue;
}
try {
args[i] = Class.forName(types[i].getClassName());
} catch (ClassNotFoundException e) {
if (types[i] == Type.BOOLEAN_TYPE) {
args[i] = boolean.class;
} else if (types[i] == Type.INT_TYPE) {
args[i] = int.class;
} else if (types[i] == Type.DOUBLE_TYPE) {
args[i] = double.class;
} else {
throw new Error(e);
}
}
}
}
public Args(Class<?>[] a) {
this.args = a;
}
@Override
public int hashCode() {
if (code != 0)
return code;
for (int i = 0; i < args.length; i++) {
code += args[i].getName().hashCode();
}
for (Class<?> c : args) {
code += c.hashCode();
}
return code;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Args) {
Args other = (Args) obj;
if (other.args.length == args.length) {
for (int i = 0; i < args.length; i++) {
if (!args[i].equals(other.args[i])) {
return false;
}
}
return true;
}
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("(");
boolean first = true;
for (Class<?> c : args) {
if (!first)
sb.append(",");
else
first = false;
sb.append(c.getName());
}
sb.append(")");
return sb.toString();
}
public Args generic() {
if (generic == null) {
Class<?>[] a = new Class[args.length];
for (int i = 0; i < args.length; i++) {
a[i] = EObject.class;
}
generic = new Args(a);
}
return generic;
}
/**
* @return
*/
public List<Args> generalize() {
ArrayList<Args> res = new ArrayList<Args>();
Class<?>[] aa = this.args.clone();
for (int i = 0; i < args.length; i++) {
// aa = this.args.clone();
// vary over arg[i]'s super types
for (Class<?> c = args[i]; !c.equals(Object.class); c = super_class(c)) {
aa[i] = c;
res.add(new Args(aa.clone()));
for (int j = i + 1; j < args.length; j++) {
for (Class<?> cc = args[j]; !cc.equals(Object.class); cc = super_class(cc)) {
aa[j] = cc;
res.add(new Args(aa.clone()));
}
}
}
}
return res;
}
private Class<?> super_class(Class<?> c) {
if (c.isPrimitive()) {
if (c == double.class)
return EDouble.class;
if (c == int.class)
return ESmall.class;
if (c == long.class)
return EBig.class;
if (c == boolean.class)
return EAtom.class;
return EObject.class;
} else {
return c.getSuperclass();
}
}
}
static class BIFHandler {
Map<Args, BuiltInFunction> found = new HashMap<Args, BuiltInFunction>();
private final String name;
private final String javaName;
@Override
public int hashCode() {
return name.hashCode() + javaName.hashCode()
+ found.values().hashCode();
}
public BIFHandler(String name) {
this.name = name;
this.javaName = name;
}
public BIFHandler(String name, String javaName) {
this.name = name;
this.javaName = javaName;
}
public Type getResult(Type[] parmTypes) {
BuiltInFunction method = getMethod(parmTypes);
if (method == null) {
throw new Error("no bif name " + this.name + " for parms "
+ new Args(parmTypes));
}
return method.getReturnType();
}
public void registerMethod(Method method) {
Args a;
Class<?>[] pt = method.getParameterTypes();
if (!Modifier.isStatic(method.getModifiers())) {
Class<?>[] all = new Class[pt.length + 1];
all[0] = method.getDeclaringClass();
System.arraycopy(pt, 0, all, 1, pt.length);
a = new Args(all);
} else {
a = new Args(pt);
}
found.put(a, new BuiltInFunction(method));
}
/**
* @param parmTypes
* @return
*/
public BuiltInFunction getMethod(Type[] parmTypes) {
BuiltInFunction m = find_bif(parmTypes);
if (m != null)
return m;
// try with EProc as first argument
if (parmTypes.length == 0 || !EPROC_TYPE.equals(parmTypes[0])) {
Type[] extra = new Type[parmTypes.length + 1];
extra[0] = EPROC_TYPE;
for (int i = 0; i < parmTypes.length; i++) {
extra[i + 1] = parmTypes[i];
}
m = find_bif(extra);
if (m != null)
return m;
}
return m;
}
private BuiltInFunction find_bif(Type[] parmTypes) {
Args args = new Args(parmTypes);
BuiltInFunction m = found.get(args);
if (m != null) {
return m;
}
for (Args a : args.generalize()) {
m = found.get(a);
if (m != null) {
if (ERT.log.isLoggable(Level.FINE))
ERT.log.fine("missed opportunity erlang:"
+ EAtom.intern(name) + "/" + parmTypes.length + " "
+ args + ", \n\tusing " + m);
return m;
}
}
return null;
}
}
public static Type getBifResult(String module, String name, Type[] parmTypes,
boolean isGuard) {
Map<String, BIFHandler> tab = isGuard ? guard_bifs : bifs;
BIFHandler bif = null;
String key = module + ":" + name;
if (tab.containsKey(key)) {
bif = tab.get(key);
} else {
throw new Error("no " + (isGuard ? "guard" : "normal")
+ " bif named "+module+":'" + name + "'/" + parmTypes.length );
}
return bif.getResult(parmTypes);
}
@SuppressWarnings("unchecked")
private static void loadBIFs(String[] mods) throws Exception {
for (String mod : mods) {
Class<ENative> en =
(Class<ENative>) Class.forName("erjang.m." + mod + ".Native");
registerBifs(mod, en);
ENative enative = en.newInstance();
for (Class<?> c : enative.getNativeClasses()) {
if (c != en) {
registerBifs(mod, c);
}
}
}
registerBifs("erlang", ERT.class);
registerBifs("erlang", EObject.class);
registerBifs("erlang", ESmall.class);
registerBifs("erlang", EBig.class);
}
public static void registerBifs(String module, Class<?> clazz) {
Method[] m = clazz.getMethods();
for (int i = 0; i < m.length; i++) {
Method method = m[i];
erjang.BIF ann = method.getAnnotation(erjang.BIF.class);
if (ann != null) {
if ((method.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
// System.out.println("non-static BIF "+method);
}
Map<String, BIFHandler> tab = ann.type() == erjang.BIF.Type.GUARD ? guard_bifs
: bifs;
String bifName = ann.name();
if (bifName.equals("__SELFNAME__")) {
bifName = method.getName();
}
String key = module + ":" + bifName;
BIFHandler h = tab.get(key);
if (h == null) {
tab.put(key, h = new BIFHandler(bifName));
}
h.registerMethod(method);
}
}
}
public static BuiltInFunction getMethod(String module, String name, int arity,
boolean isGuard, boolean fail_when_missing)
{
return getMethod(module, name, eobjectParmTypes(arity),
isGuard, fail_when_missing);
}
private static Type[] eobjectParmTypes(int length) {
// TODO: cache these!
Type[] res = new Type[length];
for (int i = 0; i < length; i++) {
res[i] = EOBJECT_TYPE;
}
return res;
}
/**
* @param name
* @param parmTypes
* @param fail_when_missing TODO
* @param b
* @return
*/
public static BuiltInFunction getMethod(String module, String name, Type[] parmTypes,
boolean isGuard, boolean fail_when_missing) {
Map<String, BIFHandler> tab = isGuard ? guard_bifs : bifs;
BIFHandler bif = null;
String key = module + ":" + name;
if (tab.containsKey(key)) {
bif = tab.get(key);
} else if (fail_when_missing) {
throw new Error("no " + (isGuard ? "guard" : "normal")
+ " bif named " + module+ ":'" + name + "'/" + parmTypes.length);
} else {
return null;
}
return bif.getMethod(parmTypes);
}
/**
* @param module TODO
* @param name
* @param args
* @param isGuard
* @param fail_when_missing TODO
* @return
*/
public static BuiltInFunction getMethod(String module, String name,
Arg[] args, boolean isGuard, boolean fail_when_missing) {
Type[] parms = new Type[args.length];
for (int i = 0; i < args.length; i++) {
parms[i] = args[i].type;
}
return getMethod(module, name, parms, isGuard, fail_when_missing);
}
public static BuiltInFunction getMethod(EAtom module, EAtom function,
int arity, boolean isGuard, boolean failWhenMissing) {
return getMethod(module.getName(), function.getName(), arity, isGuard, failWhenMissing);
}
static long all_bif_hash = 0;
public static long all_bif_hash() {
if (all_bif_hash == 0) {
for (BIFHandler b : bifs.values()) {
all_bif_hash += b.hashCode();
}
for (BIFHandler b : guard_bifs.values()) {
all_bif_hash += b.hashCode();
}
}
return all_bif_hash;
}
}