/******************************************************************************* * Copyright (c) 2009-2011 Luaj.org. All rights reserved. * <p/> * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * <p/> * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * <p/> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ package org.luaj.vm2.lib; //TODO 与3.0不一样 import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; import java.lang.reflect.Method; /** * Subclass of {@link LuaFunction} common to Java functions exposed to lua. * <p/> * To provide for common implementations in JME and JSE, * library functions are typically grouped on one or more library classes * and an opcode per library function is defined and used to key the switch * to the correct function within the library. * <p/> * Since lua functions can be called with too few or too many arguments, * and there are overloaded {@link LuaValue#call()} functions with varying * number of arguments, a Java function exposed in lua needs to handle the * argument fixup when a function is called with a number of arguments * differs from that expected. * <p/> * To simplify the creation of library functions, * there are 5 direct subclasses to handle common cases based on number of * argument values and number of return return values. * <ul> * <li>{@link ZeroArgFunction}</li> * <li>{@link OneArgFunction}</li> * <li>{@link TwoArgFunction}</li> * <li>{@link ThreeArgFunction}</li> * <li>{@link VarArgFunction}</li> * </ul> * <p/> * To be a Java library that can be loaded via {@code require}, it should have * a public constructor that returns a {@link LuaValue} that, when executed, * initializes the library. * <p/> * For example, the following code will implement a library called "hyperbolic" * with two functions, "sinh", and "cosh": * <pre> {@code * import org.luaj.vm2.LuaValue; * import org.luaj.vm2.lib.*; * <p/> * public class hyperbolic extends TwoArgFunction { * <p/> * public hyperbolic() {} * <p/> * public LuaValue call(LuaValue modname, LuaValue env) { * LuaValue library = tableOf(); * library.set( "sinh", new sinh() ); * library.set( "cosh", new cosh() ); * env.set( "hyperbolic", library ); * return library; * } * <p/> * static class sinh extends OneArgFunction { * public LuaValue call(LuaValue x) { * return LuaValue.valueOf(Math.sinh(x.checkdouble())); * } * } * <p/> * static class cosh extends OneArgFunction { * public LuaValue call(LuaValue x) { * return LuaValue.valueOf(Math.cosh(x.checkdouble())); * } * } * } * }</pre> * The default constructor is used to instantiate the library * in response to {@code require 'hyperbolic'} statement, * provided it is on Java"s class path. * This instance is then invoked with 2 arguments: the name supplied to require(), * and the environment for this function. The library may ignore these, or use * them to leave side effects in the global environment, for example. * In the previous example, two functions are created, 'sinh', and 'cosh', and placed * into a global table called 'hyperbolic' using the supplied 'env' argument. * <p/> * To test it, a script such as this can be used: * <pre> {@code * local t = require('hyperbolic') * print( 't', t ) * print( 'hyperbolic', hyperbolic ) * for k,v in pairs(t) do * print( 'k,v', k,v ) * end * print( 'sinh(.5)', hyperbolic.sinh(.5) ) * print( 'cosh(.5)', hyperbolic.cosh(.5) ) * }</pre> * <p/> * It should produce something like: * <pre> {@code * t table: 3dbbd23f * hyperbolic table: 3dbbd23f * k,v cosh function: 3dbbd128 * k,v sinh function: 3dbbd242 * sinh(.5) 0.5210953 * cosh(.5) 1.127626 * }</pre> * <p/> * See the source code in any of the library functions * such as {@link BaseLib} or {@link TableLib} for other examples. */ abstract public class LibFunction extends LuaFunction { /** * User-defined opcode to differentiate between instances of the library function class. * <p/> * Subclass will typicall switch on this value to provide the specific behavior for each function. */ public int opcode; //反射方法 public Method method; /** * The common name for this function, useful for debugging. * <p/> * Binding functions initialize this to the name to which it is bound. */ public String name; /** * Default constructor for use by subclasses */ protected LibFunction() { } public String tojstring() { return name != null ? name : super.tojstring(); } /** * Bind a set of library functions. * <p/> * An array of names is provided, and the first name is bound * with opcode = 0, second with 1, etc. * * @param env The environment to apply to each bound function * @param factory the Class to instantiate for each bound function * @param names array of String names, one for each function. * @see #bind(LuaValue, Class, String[], int) */ protected void bind(LuaValue env, Class factory, String[] names) { bind(env, factory, names, 0); } /** * Bind a set of library functions, with an offset * <p/> * An array of names is provided, and the first name is bound * with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc. * * @param env The environment to apply to each bound function * @param factory the Class to instantiate for each bound function * @param names array of String names, one for each function. * @param firstopcode the first opcode to use * @see #bind(LuaValue, Class, String[]) */ protected void bind(LuaValue env, Class factory, String[] names, int firstopcode) { try { for (int i = 0, n = names.length; i < n; i++) { LibFunction f = (LibFunction) factory.newInstance(); f.opcode = firstopcode + i; f.name = names[i]; env.set(f.name, f); } } catch (Exception e) { throw new LuaError("bind failed: " + e); } } /** * Java code generation utility to allocate storage for upvalue, leave it empty */ protected static LuaValue[] newupe() { return new LuaValue[1]; } /** * Java code generation utility to allocate storage for upvalue, initialize with nil */ protected static LuaValue[] newupn() { return new LuaValue[]{NIL}; } /** * Java code generation utility to allocate storage for upvalue, initialize with value */ protected static LuaValue[] newupl(LuaValue v) { return new LuaValue[]{v}; } public LuaValue call() { return argerror(1, "value"); } public LuaValue call(LuaValue a) { return call(); } public LuaValue call(LuaValue a, LuaValue b) { return call(a); } public LuaValue call(LuaValue a, LuaValue b, LuaValue c) { return call(a, b); } public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) { return call(a, b, c); } public Varargs invoke(Varargs args) { switch (args.narg()) { case 0: return call(); case 1: return call(args.arg1()); case 2: return call(args.arg1(), args.arg(2)); case 3: return call(args.arg1(), args.arg(2), args.arg(3)); default: return call(args.arg1(), args.arg(2), args.arg(3), args.arg(4)); } } }