Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 net.sandius.rembulan.lib.impl;
import net.sandius.rembulan.ByteString;
import net.sandius.rembulan.ByteStringBuilder;
import net.sandius.rembulan.Conversions;
import net.sandius.rembulan.LuaRuntimeException;
import net.sandius.rembulan.Ordering;
import net.sandius.rembulan.PlainValueTypeNamer;
import net.sandius.rembulan.Table;
import net.sandius.rembulan.lib.BadArgumentException;
import net.sandius.rembulan.lib.TableLib;
import net.sandius.rembulan.runtime.Dispatch;
import net.sandius.rembulan.runtime.ExecutionContext;
import net.sandius.rembulan.runtime.LuaFunction;
import net.sandius.rembulan.runtime.ResolvedControlThrowable;
import net.sandius.rembulan.runtime.ReturnBuffer;
import net.sandius.rembulan.runtime.UnresolvedControlThrowable;
import java.util.ArrayList;
public class DefaultTableLib extends TableLib {
// Functions defined in this code are among the ugliest pieces of code ever written.
// This is because we must assume that any operation that may suspend will -- and
// given that most of these functions involve a loop, this means we must be able to
// suspend and resume *in the middle* of such loops. Whenever a more complex logic
// is involved, this becomes extremely hairy -- table.sort is a particularly juicy
// example.
public DefaultTableLib() {
}
@Override
public LuaFunction _concat() {
return Concat.INSTANCE;
}
@Override
public LuaFunction _insert() {
return Insert.INSTANCE;
}
@Override
public LuaFunction _move() {
return Move.INSTANCE;
}
@Override
public LuaFunction _pack() {
return Pack.INSTANCE;
}
@Override
public LuaFunction _remove() {
return Remove.INSTANCE;
}
@Override
public LuaFunction _sort() {
return Sort.INSTANCE;
}
@Override
public LuaFunction _unpack() {
return Unpack.INSTANCE;
}
static long getLength(ReturnBuffer rbuf) {
Object o = rbuf.get0();
Long l = Conversions.integerValueOf(o);
if (l != null) {
return l.longValue();
}
else {
throw new LuaRuntimeException("object length is not an integer");
}
}
public static class Concat extends AbstractLibFunction {
public static final Concat INSTANCE = new Concat();
@Override
protected String name() {
return "concat";
}
private static class SuspendedState {
public final int state;
public final Table t;
public final ArgumentIterator args;
public final ByteString sep;
public final long i;
public final long j;
public final long k;
public final ByteStringBuilder bld;
public SuspendedState(int state, Table t, ArgumentIterator args, ByteString sep, long i, long j, long k, ByteStringBuilder bld) {
this.state = state;
this.t = t;
this.args = args;
this.sep = sep;
this.i = i;
this.j = j;
this.k = k;
this.bld = bld;
}
}
private static void appendToBuilder(ByteStringBuilder bld, long index, Object o) {
ByteString s = Conversions.stringValueOf(o);
if (s != null) {
bld.append(s);
}
else {
throw new LuaRuntimeException("invalid value ("
+ PlainValueTypeNamer.INSTANCE.typeNameOf(o)+ ") at index " + index
+ " in table for 'concat'");
}
}
private static void concatUsingRawGet(ExecutionContext context, Table t, ByteString sep, long i, long j) {
ByteStringBuilder bld = new ByteStringBuilder();
for (long k = i; k <= j; k++) {
Object o = t.rawget(k);
appendToBuilder(bld, k, o);
if (k + 1 <= j) {
bld.append(sep);
}
}
context.getReturnBuffer().setTo(bld.toByteString());
}
private static final int STATE_LEN_PREPARE = 0;
private static final int STATE_LEN_RESUME = 1;
private static final int STATE_BEFORE_LOOP = 2;
private static final int STATE_LOOP = 3;
private void run(ExecutionContext context, int state, Table t, ArgumentIterator args, ByteString sep, long i, long j, long k, ByteStringBuilder bld)
throws ResolvedControlThrowable {
try {
switch (state) {
// entry point for t with __len
case STATE_LEN_PREPARE:
state = STATE_LEN_RESUME;
Dispatch.len(context, t); // may suspend, will pass #obj through the stack
// resume point #1
case STATE_LEN_RESUME: {
// pass length in k
k = getLength(context.getReturnBuffer());
}
// entry point for t without __len; k == #t
case STATE_BEFORE_LOOP: {
long len = k;
k = 0; // clear k
// process arguments
sep = args.hasNext() && args.peek() != null ? args.nextString() : ByteString.empty();
i = args.optNextInt(1);
j = args.hasNext() && args.peek() != null ? args.nextInteger() : len;
// don't need the args any more
args = null;
// i, j are known, k is 0
// is this a clean table without __index?
if (!TableUtil.hasIndexMetamethod(t)) {
concatUsingRawGet(context, t, sep, i, j);
return;
}
// generic case: go through Dispatch and be prepared to be suspended;
// k is the index running from i to j (inclusive)
if (i <= j) {
k = i;
state = STATE_LOOP;
bld = new ByteStringBuilder(); // allocate the result accumulator
Dispatch.index(context, t, k++); // may suspend
// fall-through to state == STATE_LOOP
}
else {
// interval empty, we're done
context.getReturnBuffer().setTo(""); // return the empty string
return;
}
}
case STATE_LOOP: {
while (true) {
// k now points to the *next* item to retrieve; we've just processed
// (k - 1), need to add it to the results
Object v = context.getReturnBuffer().get0();
appendToBuilder(bld, k, v);
// we may now continue
if (k <= j) {
// append the separator
bld.append(sep);
state = STATE_LOOP;
Dispatch.index(context, t, k++); // may suspend
}
else {
break;
}
}
assert (k > j);
// we're done!
context.getReturnBuffer().setTo(bld.toByteString());
return;
}
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, args, sep, i, j, k, bld));
}
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
final Table t = args.nextTable();
final int state;
final long k;
if (!TableUtil.hasLenMetamethod(t)) {
// safe to use rawlen
state = STATE_BEFORE_LOOP;
k = t.rawlen();
}
else {
// must use Dispatch
state = STATE_LEN_PREPARE;
k = 0; // dummy value, will be overwritten
}
run(context, state, t, args, null, 0, 0, k, null);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
SuspendedState ss = (SuspendedState) suspendedState;
run(context, ss.state, ss.t, ss.args, ss.sep, ss.i, ss.j, ss.k, ss.bld);
}
}
public static class Insert extends AbstractLibFunction {
public static final Insert INSTANCE = new Insert();
@Override
protected String name() {
return "insert";
}
private static class SuspendedState {
public final int state;
public final Table t;
public final ArgumentIterator args;
public final long pos;
public final long len;
public final Object value;
private SuspendedState(int state, Table t, ArgumentIterator args, long pos, long len, Object value) {
this.state = state;
this.t = t;
this.args = args;
this.pos = pos;
this.len = len;
this.value = value;
}
}
private static final int PHASE_SHIFT = 3;
private static final int _LEN = 0;
private static final int _LOOP = 1;
private static final int _END = 2;
private static final int _LEN_PREPARE = (_LEN << PHASE_SHIFT) | 0;
private static final int _LEN_RESUME = (_LEN << PHASE_SHIFT) | 1;
private static final int _LOOP_TEST = (_LOOP << PHASE_SHIFT) | 0;
private static final int _LOOP_TABGET = (_LOOP << PHASE_SHIFT) | 1;
private static final int _LOOP_TABSET = (_LOOP << PHASE_SHIFT) | 2;
private static final int _END_TABSET = (_END << PHASE_SHIFT) | 0;
private static final int _END_RETURN = (_END << PHASE_SHIFT) | 1;
private void checkValidPos(long pos, long len) {
if (pos < 1 || pos > len + 1) {
throw new BadArgumentException(2, name(), "position out of bounds");
}
}
private static void rawInsert(Table t, long pos, long len, Object value) {
for (long k = len + 1; k > pos; k--) {
t.rawset(k, t.rawget(k - 1));
}
t.rawset(pos, value);
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table t = args.nextTable();
if (!TableUtil.hasLenMetamethod(t)) {
run_args(context, t, args, t.rawlen());
}
else {
_len(context, _LEN_PREPARE, t, args);
}
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
SuspendedState ss = (SuspendedState) suspendedState;
switch (ss.state >> PHASE_SHIFT) {
case _LEN: _len(context, ss.state, ss.t, ss.args); break;
case _LOOP: _loop(context, ss.state, ss.t, ss.pos, ss.len, ss.value); break;
case _END: _end(context, ss.state, ss.t, ss.pos, ss.value); break;
default: throw new IllegalStateException("Illegal state: " + ss.state);
}
}
private void _len(ExecutionContext context, int state, Table t, ArgumentIterator args)
throws ResolvedControlThrowable {
// __len is defined, must go through Dispatch
final long len;
try {
switch (state) {
case _LEN_PREPARE:
state = _LEN_RESUME;
Dispatch.len(context, t);
case _LEN_RESUME: {
len = getLength(context.getReturnBuffer());
break;
}
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, args, 0, 0, null));
}
// next: process the arguments
run_args(context, t, args, len);
}
private void run_args(ExecutionContext context, Table t, ArgumentIterator args, long len)
throws ResolvedControlThrowable {
final long pos;
final Object value;
if (args.size() == 2) {
// implicit pos (#t + 1)
pos = len + 1;
value = args.nextAny();
}
else if (args.size() == 3) {
// explicit pos
pos = args.nextInteger();
checkValidPos(pos, len);
value = args.nextAny();
}
else {
throw new LuaRuntimeException("wrong number of arguments to 'insert'");
}
// next: start the loop
start_loop(context, t, pos, len, value);
}
private void start_loop(ExecutionContext context, Table t, long pos, long len, Object value)
throws ResolvedControlThrowable {
// check whether we can use raw accesses instead of having to go through
// Dispatch and potential metamethods
if (!TableUtil.hasIndexMetamethod(t) && !TableUtil.hasNewIndexMetamethod(t)) {
// raw case
rawInsert(t, pos, len, value);
context.getReturnBuffer().setTo();
}
else {
// generic (Dispatch'd) case
// initialise k = len + 1 (will be decremented in the next TEST, so add +1 here)
_loop(context, _LOOP_TEST, t, pos, len + 2, value);
}
}
private void _loop(ExecutionContext context, int state, Table t, long pos, long k, Object value)
throws ResolvedControlThrowable {
// came from start_loop in the invoke path
try {
loop: while (true) {
switch (state) {
case _LOOP_TEST:
k -= 1;
state = _LOOP_TABGET;
if (k <= pos) {
break loop; // end the loop
}
case _LOOP_TABGET:
state = _LOOP_TABSET;
Dispatch.index(context, t, k - 1);
case _LOOP_TABSET:
state = _LOOP_TEST;
Object v = context.getReturnBuffer().get0();
Dispatch.setindex(context, t, k, v);
break; // go to next iteration
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, null, pos, k, value));
}
// continue into the last stage
state = _END_TABSET;
_end(context, state, t, pos, value);
}
private void _end(ExecutionContext context, int state, Table t, long pos, Object value)
throws ResolvedControlThrowable {
try {
switch (state) {
case _END_TABSET:
state = _END_RETURN;
Dispatch.setindex(context, t, pos, value);
case _END_RETURN:
break;
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, null, pos, -1, value));
}
// finished!
context.getReturnBuffer().setTo();
}
}
public static class Move extends AbstractLibFunction {
public static final Move INSTANCE = new Move();
@Override
protected String name() {
return "move";
}
private static class SuspendedState {
public final int state;
public final Table a1;
public final Table a2;
public final long f;
public final long t;
public final long idx;
public final long num;
public final boolean asc;
private SuspendedState(int state, Table a1, Table a2, long f, long t, long idx, long num, boolean asc) {
this.state = state;
this.a1 = a1;
this.a2 = a2;
this.f = f;
this.t = t;
this.idx = idx;
this.num = num;
this.asc = asc;
}
}
private void checkValidArgs(long f, long e, long t) {
if (f <= e) {
long num = e - f + 1;
if (num < 1) {
// overflow
throw new BadArgumentException(3, name(), "too many elements to move");
}
if (t + (num - 1) < t) {
throw new BadArgumentException(4, name(), "destination wrap around");
}
}
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
if (args.size() < 2) {
throw args.badArgument(2, "number expected, got no value");
}
args.goTo(1);
long f = args.nextInteger();
long e = args.nextInteger();
long t = args.nextInteger();
final Table dest;
if (args.hasNext() && args.peek() != null) {
dest = args.nextTable();
}
else {
dest = null;
}
args.goTo(0);
final Table a1 = args.nextTable();
final Table a2 = dest != null ? dest : a1;
checkValidArgs(f, e, t);
if (f <= e) {
long num = e - f + 1;
assert (num > 0);
boolean overlap = a1 == a2 && (f < t && t <= e);
if (!TableUtil.hasIndexMetamethod(a1) && !TableUtil.hasNewIndexMetamethod(a2)) {
// raw case
if (overlap) {
// same destination, range overlap
for (long idx = num - 1; idx >= 0; idx--) {
a2.rawset(t + idx, a1.rawget(f + idx));
}
}
else {
// different destination, or no range overlap
for (long idx = 0; idx < num; idx++) {
a2.rawset(t + idx, a1.rawget(f + idx));
}
}
// done
context.getReturnBuffer().setTo(a2);
}
else {
long idx = overlap ? num - 1 : 0;
_run(context, 0, a1, a2, f, t, idx, num, !overlap);
}
}
else {
// no work: done
context.getReturnBuffer().setTo(a2);
}
}
private void _run(ExecutionContext context, int state, Table a1, Table a2, long f, long t, long idx, long num, boolean asc)
throws ResolvedControlThrowable {
try {
while (true) {
switch (state) {
case 0: {
boolean done = asc ? idx >= num : idx < 0;
if (done) {
context.getReturnBuffer().setTo(a2);
return;
}
}
case 1:
state = 2;
Dispatch.index(context, a1, f + idx);
case 2:
Object v = context.getReturnBuffer().get0();
state = 3;
Dispatch.setindex(context, a2, t + idx, v);
case 3:
idx += (asc ? 1 : -1);
state = 0;
break;
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, a1, a2, f, t, idx, num, asc));
}
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
SuspendedState ss = (SuspendedState) suspendedState;
_run(context, ss.state, ss.a1, ss.a2, ss.f, ss.t, ss.idx, ss.num, ss.asc);
}
}
public static class Pack extends AbstractLibFunction {
public static final Pack INSTANCE = new Pack();
@Override
protected String name() {
return "pack";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table table = context.newTable();
int n = 0;
while (args.hasNext()) {
table.rawset(n + 1, args.nextAny());
n += 1;
}
table.rawset("n", Long.valueOf(n));
context.getReturnBuffer().setTo(table);
}
}
public static class Remove extends AbstractLibFunction {
public static final Remove INSTANCE = new Remove();
@Override
protected String name() {
return "remove";
}
private static class SuspendedState {
public final int state;
public final Table t;
public final ArgumentIterator args;
public final long pos;
public final long len;
public final Object result;
private SuspendedState(int state, Table t, ArgumentIterator args, long pos, long len, Object result) {
this.state = state;
this.t = t;
this.args = args;
this.pos = pos;
this.len = len;
this.result = result;
}
}
private static final int PHASE_SHIFT = 4;
private static final int _LEN = 0;
private static final int _GET = 1;
private static final int _LOOP = 2;
private static final int _ERASE = 4;
private static final int _LEN_PREPARE = (_LEN << PHASE_SHIFT) | 0;
private static final int _LEN_RESUME = (_LEN << PHASE_SHIFT) | 1;
private static final int _GET_PREPARE = (_GET << PHASE_SHIFT) | 0;
private static final int _GET_RESUME = (_GET << PHASE_SHIFT) | 1;
private static final int _LOOP_TEST = (_LOOP << PHASE_SHIFT) | 0;
private static final int _LOOP_TABGET = (_LOOP << PHASE_SHIFT) | 1;
private static final int _LOOP_TABSET = (_LOOP << PHASE_SHIFT) | 2;
private static final int _ERASE_PREPARE = (_ERASE << PHASE_SHIFT) | 0;
private static final int _ERASE_RESUME = (_ERASE << PHASE_SHIFT) | 1;
private void checkValidPos(long pos, long len) {
if (!((len == 0 && pos == 0) || pos == len) // the no-shift case
&& (pos < 1 || pos > len + 1)) {
throw new BadArgumentException(2, name(), "position out of bounds");
}
}
private static Object rawRemove(Table t, long pos, long len) {
Object result = t.rawget(pos);
if (pos == 0 || pos == len || pos == len + 1) {
// erase t[pos]
t.rawset(pos, null);
}
else {
// shift down t[pos+1],...,t[len]; erase t[len]
for (long k = pos + 1; k <= len; k++) {
t.rawset(k - 1, t.rawget(k));
}
t.rawset(len, null);
}
return result;
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table t = args.nextTable();
if (!TableUtil.hasLenMetamethod(t)) {
run_args(context, t, args, t.rawlen());
}
else {
_len(context, _LEN_PREPARE, t, args);
}
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
SuspendedState ss = (SuspendedState) suspendedState;
switch (ss.state >> PHASE_SHIFT) {
case _LEN: _len(context, ss.state, ss.t, ss.args); break;
case _GET: _get_result(context, ss.state, ss.t, ss.pos, ss.len); break;
case _LOOP: _loop(context, ss.state, ss.t, ss.pos, ss.len, ss.result); break;
case _ERASE: _erase(context, ss.state, ss.t, 0, ss.result); break; // index not needed any more when resuming
default: throw new IllegalStateException("Illegal state: " + ss.state);
}
}
private void _len(ExecutionContext context, int state, Table t, ArgumentIterator args)
throws ResolvedControlThrowable {
// __len is defined, must go through Dispatch
final long len;
try {
switch (state) {
case _LEN_PREPARE:
state = _LEN_RESUME;
Dispatch.len(context, t);
case _LEN_RESUME: {
len = getLength(context.getReturnBuffer());
break;
}
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, args, 0, 0, null));
}
// next: process the arguments
run_args(context, t, args, len);
}
private void run_args(ExecutionContext context, Table t, ArgumentIterator args, long len)
throws ResolvedControlThrowable {
final long pos;
if (args.hasNext() && args.peek() != null) {
// explicit pos
pos = args.nextInteger();
checkValidPos(pos, len);
}
else {
// implicit pos (#t)
pos = len;
}
// next: start the loop
start_loop(context, t, pos, len);
}
private void start_loop(ExecutionContext context, Table t, long pos, long len)
throws ResolvedControlThrowable {
// check whether we can use raw accesses instead of having to go through
// Dispatch and potential metamethods
if (!TableUtil.hasIndexMetamethod(t) && !TableUtil.hasNewIndexMetamethod(t)) {
// raw case
Object result = rawRemove(t, pos, len);
context.getReturnBuffer().setTo(result);
}
else {
// generic (Dispatch'd) case
// initialise k = len + 1 (will be decremented in the next TEST, so add +1 here)
_get_result(context, _GET_PREPARE, t, pos, len);
}
}
private void _get_result(ExecutionContext context, int state, Table t, long pos, long len)
throws ResolvedControlThrowable {
final Object result;
try {
switch (state) {
case _GET_PREPARE:
state = _GET_RESUME;
Dispatch.index(context, t, pos);
case _GET_RESUME:
result = context.getReturnBuffer().get0();
break;
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, null, pos, len, null));
}
if (pos == 0 || pos == len || pos == len + 1) {
// erase t[pos]
_erase(context, _ERASE_PREPARE, t, pos, result);
}
else {
// pos now used for iteration; want (pos + 1), but it will be incremented before the test
_loop(context, _LOOP_TEST, t, pos, len, result);
}
}
private void _loop(ExecutionContext context, int state, Table t, long k, long len, Object result)
throws ResolvedControlThrowable {
// came from start_loop in the invoke path
try {
loop: while (true) {
switch (state) {
case _LOOP_TEST:
k += 1;
state = _LOOP_TABGET;
if (k > len) {
break loop; // end the loop
}
case _LOOP_TABGET:
state = _LOOP_TABSET;
Dispatch.index(context, t, k);
case _LOOP_TABSET:
state = _LOOP_TEST;
Object v = context.getReturnBuffer().get0();
Dispatch.setindex(context, t, k - 1, v);
break; // go to next iteration
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, t, null, k, len, result));
}
// erase the last element, return the result
_erase(context, _ERASE_PREPARE, t, len, result);
}
private void _erase(ExecutionContext context, int state, Table t, long idx, Object result)
throws ResolvedControlThrowable {
try {
switch (state) {
case _ERASE_PREPARE:
state = _ERASE_RESUME;
Dispatch.setindex(context, t, idx, null);
case _ERASE_RESUME:
// we're done
break;
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
// no need to carry any information but the result around
throw ct.resolve(this, new SuspendedState(state, null, null, 0, 0, result));
}
// finished, set the result
context.getReturnBuffer().setTo(result);
}
}
public static class Sort extends AbstractLibFunction {
// implemented using heapsort
public static final Sort INSTANCE = new Sort();
@Override
protected String name() {
return "sort";
}
// States:
// +-------------------------+----------------------------+---------------------+
// | fetching length | heapifying | sorting |
// +-------------------------+----------------------------+---------------------+
// ^ ^ ^
// +- STATE_OFFSET_LEN +- STATE_OFFSET_HEAPIFY +- STATE_OFFSET_SORT
private static final int STATE_OFFSET_LEN = 0;
private static final int STATE_OFFSET_HEAPIFY = STATE_OFFSET_LEN + 2;
private static final int STATE_OFFSET_SORT = STATE_OFFSET_HEAPIFY + 2;
private static class SuspendedState {
// whenever siftState is >= 0, there is an unfinished siftDown operation;
// once completed, state is to be resumed.
public final int state;
public final int siftState;
public final ArgumentIterator args;
public final Table t;
public final LuaFunction comp;
public final long i;
public final long len;
public final long j;
public final Object o1;
public final Object o2;
public final Object o3;
private SuspendedState(int state, int siftState, ArgumentIterator args, Table t, LuaFunction comp,
long i, long len, long j, Object o1, Object o2, Object o3) {
this.state = state;
this.siftState = siftState;
this.t = t;
this.args = args;
this.comp = comp;
this.i = i;
this.len = len;
this.j = j;
this.o1 = o1;
this.o2 = o2;
this.o3 = o3;
}
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table t = args.nextTable();
if (!TableUtil.hasLenMetamethod(t)) {
long len = t.rawlen();
prepareLoop(context, args, t, len);
}
else {
fetchLen(context, STATE_OFFSET_LEN, args, t);
}
}
private void fetchLen(ExecutionContext context, int state, ArgumentIterator args, Table t)
throws ResolvedControlThrowable {
long len = 0;
try {
switch (state) {
case STATE_OFFSET_LEN:
state = STATE_OFFSET_LEN + 1;
Dispatch.len(context, t);
case STATE_OFFSET_LEN + 1:
len = getLength(context.getReturnBuffer());
break;
default:
throw new IllegalStateException("Illegal state: " + state);
}
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, new SuspendedState(state, -1, args, t, null, 1, -1, -1, null, null, null));
}
prepareLoop(context, args, t, len);
}
private void prepareLoop(ExecutionContext context, ArgumentIterator args, Table t, long len)
throws ResolvedControlThrowable {
if (len >= Integer.MAX_VALUE) {
throw args.badArgument(1, "array too big");
}
if (len < 2) {
// nothing to sort
return;
}
else {
LuaFunction comp = args.hasNext() && args.peek() != null ? args.nextFunction() : null;
// can we sort it using a raw ordering?
Ordering