mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-07-04 02:52:56 +00:00
Add support for a zero-copy Lua table
The API is entirely designed for the needs of the speaker right now, so doesn't do much else.
This commit is contained in:
parent
c4024a4c4c
commit
3eab2a9b57
@ -183,6 +183,24 @@ public interface IArguments
|
|||||||
return (Map<?, ?>) value;
|
return (Map<?, ?>) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an argument as a table in an unsafe manner.
|
||||||
|
*
|
||||||
|
* Classes implementing this interface may choose to implement a more optimised version which does not copy the
|
||||||
|
* table, instead returning a wrapper version, making it more efficient. However, the caller must guarantee that
|
||||||
|
* they do not access off the computer thread (and so should not be used with main-thread functions) or once the
|
||||||
|
* function call has finished (for instance, in callbacks).
|
||||||
|
*
|
||||||
|
* @param index The argument number.
|
||||||
|
* @return The argument's value.
|
||||||
|
* @throws LuaException If the value is not a table.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
default LuaTable<?, ?> getTableUnsafe( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
return new ObjectLuaTable( getTable( index ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an argument as a double.
|
* Get an argument as a double.
|
||||||
*
|
*
|
||||||
@ -314,6 +332,27 @@ public interface IArguments
|
|||||||
return Optional.of( (Map<?, ?>) value );
|
return Optional.of( (Map<?, ?>) value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an argument as a table in an unsafe manner.
|
||||||
|
*
|
||||||
|
* Classes implementing this interface may choose to implement a more optimised version which does not copy the
|
||||||
|
* table, instead returning a wrapper version, making it more efficient. However, the caller must guarantee that
|
||||||
|
* they do not access off the computer thread (and so should not be used with main-thread functions) or once the
|
||||||
|
* function call has finished (for instance, in callbacks).
|
||||||
|
*
|
||||||
|
* @param index The argument number.
|
||||||
|
* @return The argument's value, or {@link Optional#empty()} if not present.
|
||||||
|
* @throws LuaException If the value is not a table.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
default Optional<LuaTable<?, ?>> optTableUnsafe( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = get( index );
|
||||||
|
if( value == null ) return Optional.empty();
|
||||||
|
if( !(value instanceof Map) ) throw LuaValues.badArgumentOf( index, "map", value );
|
||||||
|
return Optional.of( new ObjectLuaTable( (Map<?, ?>) value ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an argument as a double.
|
* Get an argument as a double.
|
||||||
*
|
*
|
||||||
@ -404,4 +443,13 @@ public interface IArguments
|
|||||||
{
|
{
|
||||||
return optTable( index ).orElse( def );
|
return optTable( index ).orElse( def );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called when the current function finishes, before any main thread tasks have run.
|
||||||
|
*
|
||||||
|
* Called when the current function returns, and so some values are no longer guaranteed to be safe to access.
|
||||||
|
*/
|
||||||
|
default void releaseImmediate()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,17 @@ public @interface LuaFunction
|
|||||||
* Run this function on the main server thread. This should be specified for any method which interacts with
|
* Run this function on the main server thread. This should be specified for any method which interacts with
|
||||||
* Minecraft in a thread-unsafe manner.
|
* Minecraft in a thread-unsafe manner.
|
||||||
*
|
*
|
||||||
* @return Whether this functi
|
* @return Whether this function should be run on the main thread.
|
||||||
* @see ILuaContext#issueMainThreadTask(ILuaTask)
|
* @see ILuaContext#issueMainThreadTask(ILuaTask)
|
||||||
*/
|
*/
|
||||||
boolean mainThread() default false;
|
boolean mainThread() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow using "unsafe" arguments, such {@link IArguments#getTableUnsafe(int)}.
|
||||||
|
*
|
||||||
|
* This is incompatible with {@link #mainThread()}.
|
||||||
|
*
|
||||||
|
* @return Whether this function supports unsafe arguments.
|
||||||
|
*/
|
||||||
|
boolean unsafe() default false;
|
||||||
}
|
}
|
||||||
|
114
src/main/java/dan200/computercraft/api/lua/LuaTable.java
Normal file
114
src/main/java/dan200/computercraft/api/lua/LuaTable.java
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static dan200.computercraft.api.lua.LuaValues.*;
|
||||||
|
|
||||||
|
public interface LuaTable<K, V> extends Map<K, V>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Compute the length of the array part of this table.
|
||||||
|
*
|
||||||
|
* @return This table's length.
|
||||||
|
*/
|
||||||
|
default int length()
|
||||||
|
{
|
||||||
|
int size = 0;
|
||||||
|
while( containsKey( (double) (size + 1) ) ) size++;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array entry as an integer.
|
||||||
|
*
|
||||||
|
* @param index The index in the table, starting at 1.
|
||||||
|
* @return The table's value.
|
||||||
|
* @throws LuaException If the value is not an integer.
|
||||||
|
*/
|
||||||
|
default long getLong( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = get( (double) index );
|
||||||
|
if( !(value instanceof Number) ) throw badTableItem( index, "number", getType( value ) );
|
||||||
|
|
||||||
|
Number number = (Number) value;
|
||||||
|
double asDouble = number.doubleValue();
|
||||||
|
if( !Double.isFinite( asDouble ) ) throw badTableItem( index, "number", getNumericType( asDouble ) );
|
||||||
|
return number.longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a table entry as an integer.
|
||||||
|
*
|
||||||
|
* @param key The name of the field in the table.
|
||||||
|
* @return The table's value.
|
||||||
|
* @throws LuaException If the value is not an integer.
|
||||||
|
*/
|
||||||
|
default long getLong( String key ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = get( key );
|
||||||
|
if( !(value instanceof Number) ) throw badField( key, "number", getType( value ) );
|
||||||
|
|
||||||
|
Number number = (Number) value;
|
||||||
|
double asDouble = number.doubleValue();
|
||||||
|
if( !Double.isFinite( asDouble ) ) throw badField( key, "number", getNumericType( asDouble ) );
|
||||||
|
return number.longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array entry as an integer.
|
||||||
|
*
|
||||||
|
* @param index The index in the table, starting at 1.
|
||||||
|
* @return The table's value.
|
||||||
|
* @throws LuaException If the value is not an integer.
|
||||||
|
*/
|
||||||
|
default int getInt( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
return (int) getLong( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a table entry as an integer.
|
||||||
|
*
|
||||||
|
* @param key The name of the field in the table.
|
||||||
|
* @return The table's value.
|
||||||
|
* @throws LuaException If the value is not an integer.
|
||||||
|
*/
|
||||||
|
default int getInt( String key ) throws LuaException
|
||||||
|
{
|
||||||
|
return (int) getLong( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
default V put( K o, V o2 )
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default V remove( Object o )
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void putAll( @Nonnull Map<? extends K, ? extends V> map )
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void clear()
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
|
||||||
|
}
|
||||||
|
}
|
@ -102,6 +102,34 @@ public final class LuaValues
|
|||||||
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" );
|
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a table item exception, from an expected and actual type.
|
||||||
|
*
|
||||||
|
* @param index The index into the table, starting from 1.
|
||||||
|
* @param expected The expected type for this table item.
|
||||||
|
* @param actual The provided type for this table item.
|
||||||
|
* @return The constructed exception, which should be thrown immediately.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static LuaException badTableItem( int index, @Nonnull String expected, @Nonnull String actual )
|
||||||
|
{
|
||||||
|
return new LuaException( "table item #" + index + " is not " + expected + " (got " + actual + ")" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a field exception, from an expected and actual type.
|
||||||
|
*
|
||||||
|
* @param key The name of the field.
|
||||||
|
* @param expected The expected type for this table item.
|
||||||
|
* @param actual The provided type for this table item.
|
||||||
|
* @return The constructed exception, which should be thrown immediately.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public static LuaException badField( String key, @Nonnull String expected, @Nonnull String actual )
|
||||||
|
{
|
||||||
|
return new LuaException( "field " + key + " is not " + expected + " (got " + actual + ")" );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure a numeric argument is finite (i.e. not infinite or {@link Double#NaN}.
|
* Ensure a numeric argument is finite (i.e. not infinite or {@link Double#NaN}.
|
||||||
*
|
*
|
||||||
|
@ -5,10 +5,12 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.lua;
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link IArguments} which wraps an array of {@link Object}.
|
* An implementation of {@link IArguments} which wraps an array of {@link Object}.
|
||||||
@ -16,6 +18,8 @@ import java.util.Objects;
|
|||||||
public final class ObjectArguments implements IArguments
|
public final class ObjectArguments implements IArguments
|
||||||
{
|
{
|
||||||
private static final IArguments EMPTY = new ObjectArguments();
|
private static final IArguments EMPTY = new ObjectArguments();
|
||||||
|
|
||||||
|
private boolean released = false;
|
||||||
private final List<Object> args;
|
private final List<Object> args;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -63,4 +67,34 @@ public final class ObjectArguments implements IArguments
|
|||||||
{
|
{
|
||||||
return args.toArray();
|
return args.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public LuaTable<?, ?> getTableUnsafe( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
if( released )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot use getTableUnsafe after IArguments has been released" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return IArguments.super.getTableUnsafe( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Optional<LuaTable<?, ?>> optTableUnsafe( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
if( released )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot use optTableUnsafe after IArguments has been released" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return IArguments.super.optTableUnsafe( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseImmediate()
|
||||||
|
{
|
||||||
|
released = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ObjectLuaTable implements LuaTable<Object, Object>
|
||||||
|
{
|
||||||
|
private final Map<Object, Object> map;
|
||||||
|
|
||||||
|
public ObjectLuaTable( Map<?, ?> map )
|
||||||
|
{
|
||||||
|
this.map = Collections.unmodifiableMap( map );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey( Object o )
|
||||||
|
{
|
||||||
|
return map.containsKey( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue( Object o )
|
||||||
|
{
|
||||||
|
return map.containsKey( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get( Object o )
|
||||||
|
{
|
||||||
|
return map.get( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Set<Object> keySet()
|
||||||
|
{
|
||||||
|
return map.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Collection<Object> values()
|
||||||
|
{
|
||||||
|
return map.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<Object, Object>> entrySet()
|
||||||
|
{
|
||||||
|
return map.entrySet();
|
||||||
|
}
|
||||||
|
}
|
@ -130,8 +130,6 @@ public final class Generator<T>
|
|||||||
|
|
||||||
private void addMethod( List<NamedMethod<T>> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance )
|
private void addMethod( List<NamedMethod<T>> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance )
|
||||||
{
|
{
|
||||||
if( annotation.mainThread() ) instance = wrap.apply( instance );
|
|
||||||
|
|
||||||
String[] names = annotation.value();
|
String[] names = annotation.value();
|
||||||
boolean isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
|
boolean isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
|
||||||
if( names.length == 0 )
|
if( names.length == 0 )
|
||||||
@ -183,6 +181,13 @@ public final class Generator<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LuaFunction annotation = method.getAnnotation( LuaFunction.class );
|
||||||
|
if( annotation.unsafe() && annotation.mainThread() )
|
||||||
|
{
|
||||||
|
ComputerCraft.log.error( "Lua Method {} cannot use unsafe and mainThread", name );
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
// We have some rather ugly handling of static methods in both here and the main generate function. Static methods
|
// We have some rather ugly handling of static methods in both here and the main generate function. Static methods
|
||||||
// only come from generic sources, so this should be safe.
|
// only come from generic sources, so this should be safe.
|
||||||
Class<?> target = Modifier.isStatic( modifiers ) ? method.getParameterTypes()[0] : method.getDeclaringClass();
|
Class<?> target = Modifier.isStatic( modifiers ) ? method.getParameterTypes()[0] : method.getDeclaringClass();
|
||||||
@ -190,11 +195,13 @@ public final class Generator<T>
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
String className = method.getDeclaringClass().getName() + "$cc$" + method.getName() + METHOD_ID.getAndIncrement();
|
String className = method.getDeclaringClass().getName() + "$cc$" + method.getName() + METHOD_ID.getAndIncrement();
|
||||||
byte[] bytes = generate( className, target, method );
|
byte[] bytes = generate( className, target, method, annotation.unsafe() );
|
||||||
if( bytes == null ) return Optional.empty();
|
if( bytes == null ) return Optional.empty();
|
||||||
|
|
||||||
Class<?> klass = DeclaringClassLoader.INSTANCE.define( className, bytes, method.getDeclaringClass().getProtectionDomain() );
|
Class<?> klass = DeclaringClassLoader.INSTANCE.define( className, bytes, method.getDeclaringClass().getProtectionDomain() );
|
||||||
return Optional.of( klass.asSubclass( base ).getDeclaredConstructor().newInstance() );
|
|
||||||
|
T instance = klass.asSubclass( base ).getDeclaredConstructor().newInstance();
|
||||||
|
return Optional.of( annotation.mainThread() ? wrap.apply( instance ) : instance );
|
||||||
}
|
}
|
||||||
catch( ReflectiveOperationException | ClassFormatError | RuntimeException e )
|
catch( ReflectiveOperationException | ClassFormatError | RuntimeException e )
|
||||||
{
|
{
|
||||||
@ -205,7 +212,7 @@ public final class Generator<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private byte[] generate( String className, Class<?> target, Method method )
|
private byte[] generate( String className, Class<?> target, Method method, boolean unsafe )
|
||||||
{
|
{
|
||||||
String internalName = className.replace( ".", "/" );
|
String internalName = className.replace( ".", "/" );
|
||||||
|
|
||||||
@ -238,7 +245,7 @@ public final class Generator<T>
|
|||||||
int argIndex = 0;
|
int argIndex = 0;
|
||||||
for( java.lang.reflect.Type genericArg : method.getGenericParameterTypes() )
|
for( java.lang.reflect.Type genericArg : method.getGenericParameterTypes() )
|
||||||
{
|
{
|
||||||
Boolean loadedArg = loadArg( mw, target, method, genericArg, argIndex );
|
Boolean loadedArg = loadArg( mw, target, method, unsafe, genericArg, argIndex );
|
||||||
if( loadedArg == null ) return null;
|
if( loadedArg == null ) return null;
|
||||||
if( loadedArg ) argIndex++;
|
if( loadedArg ) argIndex++;
|
||||||
}
|
}
|
||||||
@ -285,7 +292,7 @@ public final class Generator<T>
|
|||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean loadArg( MethodVisitor mw, Class<?> target, Method method, java.lang.reflect.Type genericArg, int argIndex )
|
private Boolean loadArg( MethodVisitor mw, Class<?> target, Method method, boolean unsafe, java.lang.reflect.Type genericArg, int argIndex )
|
||||||
{
|
{
|
||||||
if( genericArg == target )
|
if( genericArg == target )
|
||||||
{
|
{
|
||||||
@ -324,7 +331,7 @@ public final class Generator<T>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = Reflect.getLuaName( Primitives.unwrap( klass ) );
|
String name = Reflect.getLuaName( Primitives.unwrap( klass ), unsafe );
|
||||||
if( name != null )
|
if( name != null )
|
||||||
{
|
{
|
||||||
mw.visitVarInsn( ALOAD, 2 + context.size() );
|
mw.visitVarInsn( ALOAD, 2 + context.size() );
|
||||||
@ -344,7 +351,7 @@ public final class Generator<T>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = arg == Object.class ? "" : Reflect.getLuaName( arg );
|
String name = arg == Object.class ? "" : Reflect.getLuaName( arg, unsafe );
|
||||||
if( name != null )
|
if( name != null )
|
||||||
{
|
{
|
||||||
if( Reflect.getRawType( method, genericArg, false ) == null ) return null;
|
if( Reflect.getRawType( method, genericArg, false ) == null ) return null;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package dan200.computercraft.core.asm;
|
package dan200.computercraft.core.asm;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.api.lua.LuaTable;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -25,7 +26,7 @@ final class Reflect
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
static String getLuaName( Class<?> klass )
|
static String getLuaName( Class<?> klass, boolean unsafe )
|
||||||
{
|
{
|
||||||
if( klass.isPrimitive() )
|
if( klass.isPrimitive() )
|
||||||
{
|
{
|
||||||
@ -39,6 +40,7 @@ final class Reflect
|
|||||||
if( klass == Map.class ) return "Table";
|
if( klass == Map.class ) return "Table";
|
||||||
if( klass == String.class ) return "String";
|
if( klass == String.class ) return "String";
|
||||||
if( klass == ByteBuffer.class ) return "Bytes";
|
if( klass == ByteBuffer.class ) return "Bytes";
|
||||||
|
if( klass == LuaTable.class && unsafe ) return "TableUnsafe";
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -59,6 +59,10 @@ class BasicFunction extends VarArgFunction
|
|||||||
}
|
}
|
||||||
throw new LuaError( "Java Exception Thrown: " + t, 0 );
|
throw new LuaError( "Java Exception Thrown: " + t, 0 );
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
arguments.releaseImmediate();
|
||||||
|
}
|
||||||
|
|
||||||
if( results.getCallback() != null )
|
if( results.getCallback() != null )
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,7 @@ import dan200.computercraft.core.tracking.Tracking;
|
|||||||
import dan200.computercraft.core.tracking.TrackingField;
|
import dan200.computercraft.core.tracking.TrackingField;
|
||||||
import dan200.computercraft.shared.util.ThreadUtils;
|
import dan200.computercraft.shared.util.ThreadUtils;
|
||||||
import org.squiddev.cobalt.*;
|
import org.squiddev.cobalt.*;
|
||||||
|
import org.squiddev.cobalt.LuaTable;
|
||||||
import org.squiddev.cobalt.compiler.CompileException;
|
import org.squiddev.cobalt.compiler.CompileException;
|
||||||
import org.squiddev.cobalt.compiler.LoadState;
|
import org.squiddev.cobalt.compiler.LoadState;
|
||||||
import org.squiddev.cobalt.debug.DebugFrame;
|
import org.squiddev.cobalt.debug.DebugFrame;
|
||||||
|
@ -69,6 +69,10 @@ class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterprete
|
|||||||
}
|
}
|
||||||
throw new LuaError( "Java Exception Thrown: " + t, 0 );
|
throw new LuaError( "Java Exception Thrown: " + t, 0 );
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
arguments.releaseImmediate();
|
||||||
|
}
|
||||||
|
|
||||||
ILuaCallback callback = results.getCallback();
|
ILuaCallback callback = results.getCallback();
|
||||||
Varargs ret = machine.toValues( results.getResult() );
|
Varargs ret = machine.toValues( results.getResult() );
|
||||||
|
141
src/main/java/dan200/computercraft/core/lua/TableImpl.java
Normal file
141
src/main/java/dan200/computercraft/core/lua/TableImpl.java
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.core.lua;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
import dan200.computercraft.api.lua.LuaValues;
|
||||||
|
import org.squiddev.cobalt.*;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static dan200.computercraft.api.lua.LuaValues.badTableItem;
|
||||||
|
import static dan200.computercraft.api.lua.LuaValues.getNumericType;
|
||||||
|
|
||||||
|
class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
||||||
|
{
|
||||||
|
private final VarargArguments arguments;
|
||||||
|
private final LuaTable table;
|
||||||
|
private Map<Object, Object> backingMap;
|
||||||
|
|
||||||
|
TableImpl( VarargArguments arguments, LuaTable table )
|
||||||
|
{
|
||||||
|
this.arguments = arguments;
|
||||||
|
this.table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
checkValid();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return table.keyCount();
|
||||||
|
}
|
||||||
|
catch( LuaError e )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length()
|
||||||
|
{
|
||||||
|
return table.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
LuaValue value = table.rawget( index );
|
||||||
|
if( !(value instanceof LuaNumber) ) throw LuaValues.badTableItem( index, "number", value.typeName() );
|
||||||
|
if( value instanceof LuaInteger ) return value.toInteger();
|
||||||
|
|
||||||
|
double number = value.toDouble();
|
||||||
|
if( !Double.isFinite( number ) ) throw badTableItem( index, "number", getNumericType( number ) );
|
||||||
|
return (long) number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
checkValid();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return table.next( Constants.NIL ).first().isNil();
|
||||||
|
}
|
||||||
|
catch( LuaError e )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private LuaValue getImpl( Object o )
|
||||||
|
{
|
||||||
|
checkValid();
|
||||||
|
if( o instanceof String ) return table.rawget( (String) o );
|
||||||
|
if( o instanceof Integer ) return table.rawget( (Integer) o );
|
||||||
|
return Constants.NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey( Object o )
|
||||||
|
{
|
||||||
|
return !getImpl( o ).isNil();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get( Object o )
|
||||||
|
{
|
||||||
|
return CobaltLuaMachine.toObject( getImpl( o ), null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private Map<Object, Object> getBackingMap()
|
||||||
|
{
|
||||||
|
checkValid();
|
||||||
|
if( backingMap != null ) return backingMap;
|
||||||
|
return backingMap = Collections.unmodifiableMap(
|
||||||
|
Objects.requireNonNull( (Map<?, ?>) CobaltLuaMachine.toObject( table, null ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue( Object o )
|
||||||
|
{
|
||||||
|
return getBackingMap().containsKey( o );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Set<Object> keySet()
|
||||||
|
{
|
||||||
|
return getBackingMap().keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Collection<Object> values()
|
||||||
|
{
|
||||||
|
return getBackingMap().values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<Object, Object>> entrySet()
|
||||||
|
{
|
||||||
|
return getBackingMap().entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkValid()
|
||||||
|
{
|
||||||
|
if( arguments.released )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot use LuaTable after IArguments has been released" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ class VarargArguments implements IArguments
|
|||||||
{
|
{
|
||||||
static final IArguments EMPTY = new VarargArguments( Constants.NONE );
|
static final IArguments EMPTY = new VarargArguments( Constants.NONE );
|
||||||
|
|
||||||
|
boolean released;
|
||||||
private final Varargs varargs;
|
private final Varargs varargs;
|
||||||
private Object[] cache;
|
private Object[] cache;
|
||||||
|
|
||||||
@ -98,4 +99,39 @@ class VarargArguments implements IArguments
|
|||||||
LuaString str = ((LuaBaseString) value).strvalue();
|
LuaString str = ((LuaBaseString) value).strvalue();
|
||||||
return Optional.of( ByteBuffer.wrap( str.bytes, str.offset, str.length ).asReadOnlyBuffer() );
|
return Optional.of( ByteBuffer.wrap( str.bytes, str.offset, str.length ).asReadOnlyBuffer() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public dan200.computercraft.api.lua.LuaTable<?, ?> getTableUnsafe( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
if( released )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot use getTableUnsafe after IArguments has been released" );
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaValue value = varargs.arg( index + 1 );
|
||||||
|
if( !(value instanceof LuaTable) ) throw LuaValues.badArgument( index, "table", value.typeName() );
|
||||||
|
return new TableImpl( this, (LuaTable) value );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Optional<dan200.computercraft.api.lua.LuaTable<?, ?>> optTableUnsafe( int index ) throws LuaException
|
||||||
|
{
|
||||||
|
if( released )
|
||||||
|
{
|
||||||
|
throw new IllegalStateException( "Cannot use optTableUnsafe after IArguments has been released" );
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaValue value = varargs.arg( index + 1 );
|
||||||
|
if( value.isNil() ) return Optional.empty();
|
||||||
|
if( !(value instanceof LuaTable) ) throw LuaValues.badArgument( index, "table", value.typeName() );
|
||||||
|
return Optional.of( new TableImpl( this, (LuaTable) value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseImmediate()
|
||||||
|
{
|
||||||
|
released = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ public class ComputerTestDelegate
|
|||||||
|
|
||||||
if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );
|
if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );
|
||||||
|
|
||||||
Terminal term = new Terminal( 80, 30 );
|
Terminal term = new Terminal( 80, 100 );
|
||||||
IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
|
IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
|
||||||
|
|
||||||
// Remove any existing files
|
// Remove any existing files
|
||||||
|
@ -120,6 +120,13 @@ public class GeneratorTest
|
|||||||
contramap( notNullValue(), "callback", MethodResult::getCallback ) );
|
contramap( notNullValue(), "callback", MethodResult::getCallback ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsafe()
|
||||||
|
{
|
||||||
|
List<NamedMethod<LuaMethod>> methods = LuaMethod.GENERATOR.getMethods( Unsafe.class );
|
||||||
|
assertThat( methods, contains( named( "withUnsafe" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
public static class Basic
|
public static class Basic
|
||||||
{
|
{
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
@ -222,6 +229,21 @@ public class GeneratorTest
|
|||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Unsafe
|
||||||
|
{
|
||||||
|
@LuaFunction( unsafe = true )
|
||||||
|
public final void withUnsafe( LuaTable<?, ?> table )
|
||||||
|
{}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final void withoutUnsafe( LuaTable<?, ?> table )
|
||||||
|
{}
|
||||||
|
|
||||||
|
@LuaFunction( unsafe = true, mainThread = true )
|
||||||
|
public final void invalid( LuaTable<?, ?> table )
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> T find( Collection<NamedMethod<T>> methods, String name )
|
private static <T> T find( Collection<NamedMethod<T>> methods, String name )
|
||||||
{
|
{
|
||||||
return methods.stream()
|
return methods.stream()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user