/** * 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; import java.io.IOException; import kilim.Pausable; import erjang.beam.EUtil; import erjang.m.ets.EMatchContext; import erjang.m.ets.ETermPattern; /** Representation of an Erlang function object. * At present this class also contains a lot of code generation code for * EFun's dynamically-created subclasses. * * Please see <code>doc/RuntimeClassHierarchy.md</code> for more about * the dynamically-created classes. */ public abstract class EFun extends EObject { public abstract int arity(); @BIF public EAtom is_function(EObject arity) { ESmall ary; if ((ary=arity.testSmall()) != null) { return ERT.box( arity() == ary.value ); } throw ERT.badarg(this, arity); } public EFun testFunction2(int nargs) { if (this.arity() == nargs) return this; return null; } public EFun testFunction() { return this; } @Override int cmp_order() { return CMP_ORDER_FUN; } /* (non-Javadoc) * @see erjang.EObject#compare_same(erjang.EObject) */ @Override int compare_same(EObject rhs) { if (rhs == this) return 0; return (System.identityHashCode(this)&0xffff) - (System.identityHashCode(rhs)&0xffff); } /** used for translation of tail recursive methods */ public EObject go(EProc eproc) throws Pausable { return go2(eproc); } /** used for translation of tail recursive methods */ public EObject go2(EProc eproc) { throw new java.lang.AbstractMethodError(this.getClass().getName() + "#go2(EProc)"); } /** * @param a * @return */ public EObject apply(EProc proc, ESeq a) throws Pausable { // TODO: this should be implemented for all EFunX return invoke(proc, a.toArray()); } /** generic invoke, used only for apply */ public abstract EObject invoke(EProc proc, EObject[] args) throws Pausable; /** pass-thru handler for interpreter */ public EObject invoke(EProc proc, EObject[] args, int off, int len) throws Pausable { EObject[] new_args = new EObject[len]; System.arraycopy(args, off, new_args, 0, len); return invoke(proc, new_args); } public boolean match(ETermPattern matcher, EMatchContext r) { return matcher.match(this, r); } /** * @param eInputStream * @return * @throws IOException */ public static EFun read(EInputStream eis) throws IOException { return eis.read_fun(); } /** * @param spec * @return */ public EObject info(EAtom spec) { FunID id = get_id(); if (spec == ERT.am_arity) { return new ETuple2(spec, ERT.box(id.arity - get_env().length())); } else if (spec == ERT.am_module) { return new ETuple2(spec, id.module); } else if (spec == ERT.am_name) { return new ETuple2(spec, id.function); } else if (spec == ERT.am_env) { return new ETuple2(spec, this.get_env()); } else if (spec == ERT.am_type) { return new ETuple2(ERT.am_type, (id instanceof LocalFunID) ? ERT.am_local : ERT.am_external); } if (id instanceof LocalFunID) { LocalFunID lid = (LocalFunID) id; if (spec == ERT.am_index) { return new ETuple2(spec, ERT.box(lid.index)); } else if (spec == ERT.am_new_index) { return new ETuple2(spec, ERT.box(lid.new_index)); } else if (spec == ERT.am_uniq) { return new ETuple2(spec, ERT.box(lid.uniq)); } else if (spec == ERT.am_new_uniq) { return new ETuple2(spec, lid.new_uniq); } else if (spec == ERT.am_pid) { return new ETuple2(spec, this.get_pid()); } } else { if (spec == ERT.am_type) { return new ETuple2(ERT.am_type, ERT.am_external); } } return ERT.am_undefined; } public boolean is_local() { return this.get_id() instanceof LocalFunID; } protected EObject get_pid() { return ERT.NIL; } protected ESeq get_env() { return ERT.NIL; } @Override public String toString() { FunID id = get_id(); if (id instanceof LocalFunID) { LocalFunID lid = (LocalFunID) id; return "#Fun<" + id.module + "." + lid.index + "." + lid.uniq + ">"; } else { return "#Fun<" + id.module + ":" + id.function + "/" + id.arity + ">"; } } private static final String ERJANG_MODULES_DOT = "erjang.m."; protected FunID get_id() { String cname = getClass().getName(); EAtom module = null; if (cname.startsWith(ERJANG_MODULES_DOT)) { int last = cname.lastIndexOf('.'); String name = cname.substring(ERJANG_MODULES_DOT.length(), last); module = EAtom.intern(EUtil.decodeJavaName(name)); } EAtom fun = null; int end = cname.lastIndexOf("__"); int start = cname.indexOf("$FN_"); if (start != 1 && end != -1) { String method_name = cname.substring(start+4, end); fun = EAtom.intern(EUtil.decodeJavaName(method_name)); } return new FunID(module, fun, arity()); } }