root/trunk/ProjectFortress/src/com/sun/fortress/interpreter/glue/NativeApp.java

Revision 3998, 8.5 KB (checked in by EricAllen, 4 months ago)

Cleaned up some code formatting.

Line 
1/*******************************************************************************
2 Copyright 2009 Sun Microsystems, Inc.,
3 4150 Network Circle, Santa Clara, California 95054, U.S.A.
4 All rights reserved.
5
6 U.S. Government Rights - Commercial software.
7 Government users are subject to the Sun Microsystems, Inc. standard
8 license agreement and applicable provisions of the FAR and its supplements.
9
10 Use is subject to license terms.
11
12 This distribution may include materials developed by third parties.
13
14 Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered
15 trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
16 ******************************************************************************/
17
18package com.sun.fortress.interpreter.glue;
19
20import static com.sun.fortress.exceptions.InterpreterBug.bug;
21import static com.sun.fortress.exceptions.ProgramError.error;
22import static com.sun.fortress.exceptions.ProgramError.errorMsg;
23import com.sun.fortress.interpreter.evaluator.values.FValue;
24import com.sun.fortress.interpreter.evaluator.values.NativeConstructor;
25import com.sun.fortress.nodes.*;
26import com.sun.fortress.nodes_util.NodeFactory;
27import com.sun.fortress.nodes_util.NodeUtil;
28import com.sun.fortress.useful.Pair;
29import edu.rice.cs.plt.tuple.Option;
30
31import java.util.Hashtable;
32import java.util.List;
33import java.util.Map;
34
35/**
36 * A NativeApp indicates that an action is implemented natively; the
37 * type itself is abstract.
38 * <p/>
39 * Programmers writing a NativeApp should implement getArity and ApplyToArgs.
40 * <p/>
41 * We might want some way to sanity-check the expected type of a
42 * native thing against the actual type declared in the applicable
43 * object passed in.  Right now we just check arities.
44 */
45public abstract class NativeApp implements Applicable {
46    protected Applicable a;
47
48    /**
49     * A NativeApp has a fixed arity by default.  This defines that
50     * fixed arity.  This is used in the checking performed by
51     * setParams and by applyInner.  If you need a multi-arity
52     * function you will need to override those two methods as
53     * well.
54     */
55    public abstract int getArity();
56
57    /**
58     * Set the delegate to a, after performing an arity check and
59     * making sure the return type is defined.  Override this only if
60     * defining a function whose arity is not fixed, or you wish to
61     * perform additional sanity checks on the Fortress-side function
62     * definition.
63     */
64    protected void init(Applicable app, boolean isFunctionalMethod) {
65        if (this.a != null) {
66            bug("Duplicate NativeApp.init call.");
67        }
68        this.a = app;
69        int aty = NodeUtil.getParams(app).size();
70        // Dock functional methods by 1 for the self parameter.
71        // This lets us treat methods and functional methods identically
72        // on the native Java side, which simplifies life immensely.
73        if (isFunctionalMethod) aty--;
74        if (aty != getArity()) {
75            error(app, "Arity of type " + aty + " does not match native arity " + getArity());
76        }
77        if (NodeUtil.getReturnType(app) == null || NodeUtil.getReturnType(app).isNone()) {
78            error(app, "Please specify a Fortress return type.");
79        }
80    }
81
82    /* Except for getBody() these just delegate to a. */
83    public Expr getBody() {
84        return null;
85    }
86
87    public FnHeader getHeader() {
88        return a.getHeader();
89    }
90
91    public List<Param> getParams() {
92        return a.getHeader().getParams();
93    }
94
95    public Option<Type> getReturnType() {
96        return a.getHeader().getReturnType();
97    }
98
99    public List<StaticParam> getStaticParams() {
100        return a.getHeader().getStaticParams();
101    }
102
103    public IdOrOpOrAnonymousName getName() {
104        return a.getHeader().getName();
105    }
106
107    public Option<WhereClause> getWhereClause() {
108        return a.getHeader().getWhereClause();
109    }
110
111    public String at() {
112        return a.at();
113    }
114
115    public String stringName() {
116        return a.stringName();
117    }
118
119    public String toString() {
120        return (a.stringName() + "(native " + this.getClass().getSimpleName() + ")");
121    }
122
123    public <RetType> RetType accept(NodeVisitor<RetType> visitor) {
124        return visitor.forId(NodeFactory.makeId(NodeFactory.makeSpan(""), ""));
125    }
126
127    public void accept(NodeVisitor_void visitor) {
128    }
129
130    public int generateHashCode() {
131        return 0;
132    }
133
134    public String serialize() {
135        return bug(this, errorMsg("Cannot serialize NativeApp ", this));
136    }
137
138    public void serialize(java.io.Writer writer) {
139        bug(this, errorMsg("Cannot serialize NativeApp ", this));
140    }
141
142    public void walk(TreeWalker w) {
143        bug(this, errorMsg("Cannot walk NativeApp ", this));
144    }
145
146    /**
147     * Actually apply the native function to the passed-in arguments.
148     * Called by Closure.applyInner.  Arity and type checking have already
149     * occurred and can be assumed.  For fixed-arity primitives this
150     * method ought to simply unpack the arguments and chain to
151     * another method defined in the subclass.  RuntimeExceptions and
152     * Errors are caught and wrapped by applyInner, which also adds
153     * location information to ProgramErrors.
154     */
155    public abstract FValue applyToArgs(List<FValue> args);
156
157    /* Does the passed-in method body represent a native function?  If
158     * so, return the corresponding native action.
159     * Otherwise, return null.
160     *
161     * Right now this does a naive pattern-match for the expression:
162     *
163     * builtinPrimitive("name.of.java.class.as.literal.String")
164     *
165     * Using Java reflection we load and construct an instance of this
166     * class, failing somewhat-gracefully if any part of the attempt
167     * fails.  The error checking is gratuitously detailed so the
168     * library hacker can sort out what is going on when things break.
169     */
170    public static Applicable checkAndLoadNative(Applicable defn, boolean isFunctionalMethod) {
171        Option<Expr> optBody = NodeUtil.getBody(defn);
172        if (optBody.isNone()) return defn;
173        Expr body = optBody.unwrap();
174        Expr fn;
175        Expr arg;
176        if (body instanceof Juxt && ((Juxt) body).isTight()) {
177            List<Expr> juxts = ((Juxt) body).getExprs();
178            if (juxts.size() != 2) return defn;
179            fn = juxts.get(0);
180            arg = juxts.get(1);
181        } else if (body instanceof MathPrimary) {
182            MathPrimary mp = (MathPrimary) body;
183            List<MathItem> args = mp.getRest();
184            if (args.size() != 1) return defn;
185            fn = mp.getFront();
186            MathItem mi = args.get(0);
187            if (mi instanceof ExprMI) arg = ((ExprMI) mi).getExpr();
188            else return defn;
189        } else // (!(body instanceof TightJuxt || body instanceof MathPrimary))
190            return defn;
191        if (!(fn instanceof VarRef)) return defn;
192        if (!(arg instanceof StringLiteralExpr)) return defn;
193        Id name = ((VarRef) fn).getVarId();
194        if (!name.getText().equals("builtinPrimitive")) return defn;
195        String str = ((StringLiteralExpr) arg).getText();
196        Pair<String, Applicable> key = new Pair<String, Applicable>(str, defn);
197        synchronized (cache) {
198            NativeApp res = cache.get(key);
199            if (res != null) return res;
200            try {
201                // System.err.println("Loading primitive class "+str);
202                Class nativeAct = Class.forName(str);
203                res = (NativeApp) nativeAct.newInstance();
204                res.init(defn, isFunctionalMethod);
205                cache.put(key, res);
206                return res;
207            }
208            catch (java.lang.ClassNotFoundException x) {
209                return bug(defn, "Native class " + str + " not found.", x);
210            }
211            catch (java.lang.InstantiationException x) {
212                return bug(defn, "Native class " + str + " has no nullary constructor.", x);
213            }
214            catch (java.lang.IllegalAccessException x) {
215                return bug(defn, "Native class " + str + " cannot be accessed.", x);
216            }
217            catch (java.lang.ClassCastException x) {
218                return bug(defn, "Native class " + str + " is not a NativeApp.", x);
219            }
220        }
221    }
222
223    public static Applicable checkAndLoadNative(Applicable defn) {
224        return checkAndLoadNative(defn, false);
225    }
226
227    static public void reset() {
228        cache = new Hashtable<Pair<String, Applicable>, NativeApp>();
229        NativeConstructor.unregisterAllConstructors();
230    }
231
232    static Map<Pair<String, Applicable>, NativeApp> cache;
233}
Note: See TracBrowser for help on using the browser.