commonMain.org.luaj.vm2.lib.LibFunction.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of luak-jvm Show documentation
Show all versions of luak-jvm Show documentation
LUAK - Kotlin port of LuaJ (fork of https://github.com/korlibs/korge-luak)
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* 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:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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
import org.luaj.vm2.LuaError
import org.luaj.vm2.LuaFunction
import org.luaj.vm2.LuaValue
import org.luaj.vm2.Varargs
import kotlin.jvm.*
import kotlin.reflect.*
/**
* Subclass of [LuaFunction] common to Java functions exposed to lua.
*
*
* 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.
*
*
* Since lua functions can be called with too few or too many arguments,
* and there are overloaded [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.
*
*
* 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.
*
* * [ZeroArgFunction]
* * [OneArgFunction]
* * [TwoArgFunction]
* * [ThreeArgFunction]
* * [VarArgFunction]
*
*
*
* To be a Java library that can be loaded via `require`, it should have
* a public constructor that returns a [LuaValue] that, when executed,
* initializes the library.
*
*
* For example, the following code will implement a library called "hyperbolic"
* with two functions, "sinh", and "cosh":
* `import org.luaj.vm2.LuaValue;
* import org.luaj.vm2.lib.*;
*
* public class hyperbolic extends TwoArgFunction {
*
* public hyperbolic() {}
*
* 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;
* }
*
* static class sinh extends OneArgFunction {
* public LuaValue call(LuaValue x) {
* return LuaValue.valueOf(Math.sinh(x.checkdouble()));
* }
* }
*
* static class cosh extends OneArgFunction {
* public LuaValue call(LuaValue x) {
* return LuaValue.valueOf(Math.cosh(x.checkdouble()));
* }
* }
* }
`
*
* The default constructor is used to instantiate the library
* in response to `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.
*
*
* To test it, a script such as this can be used:
* `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) )
`
*
*
*
* It should produce something like:
* `t table: 3dbbd23f
* hyperbolic table: 3dbbd23f
* k,v cosh function: 3dbbd128
* k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953
* cosh(.5) 1.127626
`
*
*
*
* See the source code in any of the library functions
* such as [BaseLib] or [TableLib] for other examples.
*/
abstract class LibFunction
/** Default constructor for use by subclasses */
protected constructor() : LuaFunction() {
/** User-defined opcode to differentiate between instances of the library function class.
*
*
* Subclass will typicall switch on this value to provide the specific behavior for each function.
*/
@kotlin.jvm.JvmField
protected var opcode: Int = 0
/** The common name for this function, useful for debugging.
*
*
* Binding functions initialize this to the name to which it is bound.
*/
@kotlin.jvm.JvmField
protected var name: String? = null
override fun tojstring(): String {
return if (name != null) name!! else super.tojstring()
}
/**
* Bind a set of library functions, with an offset
*
*
* An array of names is provided, and the first name is bound
* with opcode = `firstopcode`, second with `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
*/
@JvmOverloads
protected fun bind(env: LuaValue, factory: () -> LibFunction, names: Array, firstopcode: Int = 0) {
try {
var i = 0
val n = names.size
while (i < n) {
val f = factory()
f.opcode = firstopcode + i
f.name = names[i]
env[f.name!!] = f
i++
}
} catch (e: Exception) {
throw LuaError("bind failed: $e")
}
}
override fun call(): LuaValue {
return LuaValue.argerror(1, "value")
}
override fun call(a: LuaValue): LuaValue {
return call()
}
override fun call(a: LuaValue, b: LuaValue): LuaValue {
return call(a)
}
override fun call(a: LuaValue, b: LuaValue, c: LuaValue): LuaValue {
return call(a, b)
}
open fun call(a: LuaValue, b: LuaValue, c: LuaValue, d: LuaValue): LuaValue {
return call(a, b, c)
}
override fun invoke(args: Varargs): Varargs {
when (args.narg()) {
0 -> return call()
1 -> return call(args.arg1())
2 -> return call(args.arg1(), args.arg(2))
3 -> return call(args.arg1(), args.arg(2), args.arg(3))
else -> return call(args.arg1(), args.arg(2), args.arg(3), args.arg(4))
}
}
companion object {
/** Java code generation utility to allocate storage for upvalue, leave it empty */
protected fun newupe(): Array {
return arrayOfNulls(1) as Array
}
/** Java code generation utility to allocate storage for upvalue, initialize with nil */
protected fun newupn(): Array {
return arrayOf(LuaValue.NIL)
}
/** Java code generation utility to allocate storage for upvalue, initialize with value */
protected fun newupl(v: LuaValue): Array {
return arrayOf(v)
}
}
}
/**
* Bind a set of library functions.
*
*
* 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
*/