org.classdump.luna.standalone.Aux Maven / Gradle / Ivy
/*
* Copyright 2016 Miroslav Janíček
*
* 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 org.classdump.luna.standalone;
import org.classdump.luna.impl.NonsuspendableFunctionException;
import org.classdump.luna.runtime.AbstractFunction0;
import org.classdump.luna.runtime.AbstractFunction2;
import org.classdump.luna.runtime.AbstractFunction3;
import org.classdump.luna.runtime.AbstractFunctionAnyArg;
import org.classdump.luna.runtime.Dispatch;
import org.classdump.luna.runtime.ExecutionContext;
import org.classdump.luna.runtime.LuaFunction;
import org.classdump.luna.runtime.ResolvedControlThrowable;
import org.classdump.luna.runtime.UnresolvedControlThrowable;
import java.util.Arrays;
import java.util.Objects;
/**
* Basic utility functions.
*/
public final class Aux {
private Aux() {
// not to be instantiated
}
/**
* Returns a vararg function that calls the result of the Lua expression {@code x()}
* with the remaining supplied arguments.
*
* In Lua terms, the returned function is equivalent to:
*
* function (...)
* return x()(...)
* end
*
* where {@code x} is the argument supplied to this method.
*
* @param x the argument to evaluate as a function, may be {@code null}
* @return a vararg function that calls the result of {@code x()} with the remaining
* arguments
*/
public static LuaFunction lift(Object x) {
return new Lift(x);
}
/**
* Returns a vararg function that returns the results of the call {@code x(y1,...,yn, ...)},
* where {@code yi} is the {@code i-th} value in {@code ys}. In other words, the function
* inserts {@code ys} to the argument list before the vararg arguments, and calls {@code f}
* with the resulting arguments.
*
* For illustration, consider the case in which {@code ys} consists of two values
* {@code y1} and {@code y2}. Then the function returned by this method this is equivalent
* to:
*
* function (...)
* return x(y1, y2, ...)
* end
*
*
* For empty {@code ys}, the resulting function is equivalent to:
*
* function (...)
* return x(...)
* end
*
*
* @param x the call target, may be {@code null}
* @param ys the arguments to insert to the argument list, must not be {@code null}
* @return a vararg function that inserts {@code ys} to the argument list and calls {@code x}
*
* @throws NullPointerException if {@code ys} is {@code null}
*/
public static LuaFunction bind(Object x, Object... ys) {
return new Bind(x, Arrays.copyOf(ys, ys.length));
}
/**
* Returns a 0-ary function that returns the results of the call {@code x(y1,...,yn)},
* where {@code yi} is the {@code i}-th value in {@code ys}.
*
* For example, if {@code ys} consists of two values {@code y1} and {@code y2},
* the returned function is equivalent to:
*
* function ()
* return x(y1, y2)
* end
*
*
* @param x the call target, may be {@code null}
* @param ys the arguments to call {@code x} with, must not be {@code null}
* @return a 0-ary function that returns the results of {@code x(ys)}
*
* @throws NullPointerException if {@code ys} is {@code null}
*/
public static LuaFunction call(Object x, Object... ys) {
return new Call(bind(x, ys));
}
/**
* Returns a function of two arguments {@code t} and {@code k} that returns the result
* of the Lua expression {@code t[k]}.
*
* In other words, the returned function is equivalent to:
*
* function (t, k)
* return t[k]
* end
*
*
* @return a function of two arguments that performs a table lookup
*/
public static LuaFunction index() {
return Index.INSTANCE;
}
/**
* Returns a 0-ary function that returns the result of the Lua expression {@code t[k]},
* including metamethod processing.
*
* In other words, the returned function is equivalent to:
*
* function ()
* return t[k]
* end
*
* where {@code t} and {@code k} are the arguments supplied to this method.
*
* @param t the object to index, may be {@code null}
* @param k the key to look up in {@code t}, may be {@code null}
* @return a function that returns the result of {@code t[k]}
*/
public static LuaFunction index(Object t, Object k) {
return call(index(), t, k);
}
/**
* Returns a function of three arguments {@code t}, {@code k} and {@code v} that
* executes the Lua statement {@code t[k] = v}.
*
* In Lua terms, the returned function is equivalent to:
*
* function (t, k, v)
* t[k] = v
* end
*
*
* @return a ternary function that performs table assignment
*/
public static LuaFunction setIndex() {
return SetIndex.INSTANCE;
}
/**
* Returns a 0-ary function that executes the Lua statement {@code t[k] = v}.
*
* In Lua terms, the returned function is equivalent to:
*
* function ()
* t[k] = v
* end
*
* where {@code t}, {@code k} and {@code v} are the arguments to this method.
*
* @param t the object to assign to, may be {@code null}
* @param k the key to assign, may be {@code null}
* @param v the value to assign, may be {@code null}
* @return a 0-ary function that performs table assignment
*/
public static LuaFunction setIndex(Object t, Object k, Object v) {
return call(setIndex(), t, k, v);
}
/**
* Returns a new vararg function that looks up {@code name} in {@code env},
* and calls the result of the lookup with the supplied arguments. Neither operation is
* raw, i.e., it both the lookup and call may involve metamethod processing.
*
* In Lua terms, the returned function is equivalent to:
*
* function (...)
* return env[name](...)
* end
*
* where {@code env} and {@code name} are the arguments supplied to this method.
*
* @param env the environment to look up {@code name} in, may be {@code null}
* @param name the name to look up in {@code env}, may be {@code null}
* @return a vararg function that looks up its first argument in {@code env} and calls it
* with the remaining arguments
*/
public static LuaFunction callGlobal(Object env, Object name) {
return lift(index(env, name));
}
static class Lift extends AbstractFunctionAnyArg {
private final Object x;
public Lift(Object x) {
this.x = x;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Lift that = (Lift) o;
return Objects.equals(x, that.x);
}
@Override
public int hashCode() {
return Objects.hash(getClass(), x);
}
@Override
public void invoke(ExecutionContext context, Object[] args) throws ResolvedControlThrowable {
try {
Dispatch.call(context, x);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, Arrays.copyOf(args, args.length));
}
resume(context, args);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
Object target = context.getReturnBuffer().get0();
context.getReturnBuffer().setToCallWithContentsOf(target, (Object[]) suspendedState);
}
}
static class Bind extends AbstractFunctionAnyArg {
private final Object fn;
private final Object[] curriedArgs;
public Bind(Object fn, Object[] args) {
this.fn = fn; // may be null
this.curriedArgs = Objects.requireNonNull(args);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Bind that = (Bind) o;
return Objects.equals(fn, that.fn) && Arrays.equals(curriedArgs, that.curriedArgs);
}
@Override
public int hashCode() {
int result = getClass().hashCode();
result = 31 * result + (fn != null ? fn.hashCode() : 0);
result = 31 * result + Arrays.hashCode(curriedArgs);
return result;
}
@Override
public void invoke(ExecutionContext context, Object[] args) throws ResolvedControlThrowable {
Object[] callArgs = new Object[curriedArgs.length + args.length];
System.arraycopy(curriedArgs, 0, callArgs, 0, curriedArgs.length);
System.arraycopy(args, 0, callArgs, curriedArgs.length, args.length);
context.getReturnBuffer().setToCallWithContentsOf(fn, callArgs);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
throw new NonsuspendableFunctionException(getClass());
}
}
static class Call extends AbstractFunction0 {
private final Object x;
public Call(Object x) {
this.x = x;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Call that = (Call) o;
return Objects.equals(x, that.x);
}
@Override
public int hashCode() {
return Objects.hash(getClass(), x);
}
@Override
public void invoke(ExecutionContext context) throws ResolvedControlThrowable {
context.getReturnBuffer().setToCall(x);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
throw new NonsuspendableFunctionException(this.getClass());
}
}
static class Index extends AbstractFunction2 {
static final Index INSTANCE = new Index();
@Override
public void invoke(ExecutionContext context, Object t, Object k) throws ResolvedControlThrowable {
try {
Dispatch.index(context, t, k);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, null);
}
resume(context, null);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
Object result = context.getReturnBuffer().get0();
context.getReturnBuffer().setTo(result);
}
}
static class SetIndex extends AbstractFunction3 {
static final SetIndex INSTANCE = new SetIndex();
@Override
public void invoke(ExecutionContext context, Object t, Object k, Object v) throws ResolvedControlThrowable {
try {
Dispatch.setindex(context, t, k, v);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, null);
}
resume(context, null);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
context.getReturnBuffer().setTo();
}
}
}