mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Refactoring RTTI, add type annotation to array subscription instructions
This commit is contained in:
parent
73d4a389b7
commit
164ebeb629
27
core/src/main/java/org/teavm/ast/ArrayType.java
Normal file
27
core/src/main/java/org/teavm/ast/ArrayType.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* 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.teavm.ast;
|
||||
|
||||
public enum ArrayType {
|
||||
BYTE,
|
||||
SHORT,
|
||||
CHAR,
|
||||
INT,
|
||||
LONG,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
OBJECT
|
||||
}
|
|
@ -75,10 +75,11 @@ public abstract class Expr implements Cloneable {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static Expr subscript(Expr array, Expr index) {
|
||||
public static Expr subscript(Expr array, Expr index, ArrayType type) {
|
||||
SubscriptExpr expr = new SubscriptExpr();
|
||||
expr.setArray(array);
|
||||
expr.setIndex(index);
|
||||
expr.setType(type);
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,10 @@ package org.teavm.ast;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class SubscriptExpr extends Expr {
|
||||
private Expr array;
|
||||
private Expr index;
|
||||
private ArrayType type;
|
||||
|
||||
public Expr getArray() {
|
||||
return array;
|
||||
|
@ -41,6 +38,14 @@ public class SubscriptExpr extends Expr {
|
|||
this.index = index;
|
||||
}
|
||||
|
||||
public ArrayType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(ArrayType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(ExprVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
|
@ -55,6 +60,7 @@ public class SubscriptExpr extends Expr {
|
|||
SubscriptExpr copy = new SubscriptExpr();
|
||||
copy.setArray(array != null ? array.clone(cache) : null);
|
||||
copy.setIndex(index != null ? index.clone(cache) : null);
|
||||
copy.setType(type);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.ast.ArrayType;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.BinaryOperation;
|
||||
import org.teavm.ast.BreakStatement;
|
||||
|
@ -49,6 +50,7 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
|
@ -442,18 +444,42 @@ class StatementGenerator implements InstructionVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(GetElementInstruction insn) {
|
||||
assign(Expr.subscript(Expr.var(insn.getArray().getIndex()), Expr.var(insn.getIndex().getIndex())),
|
||||
insn.getReceiver());
|
||||
Expr subscript = Expr.subscript(Expr.var(insn.getArray().getIndex()), Expr.var(insn.getIndex().getIndex()),
|
||||
map(insn.getType()));
|
||||
assign(subscript, insn.getReceiver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PutElementInstruction insn) {
|
||||
AssignmentStatement stmt = Statement.assign(Expr.subscript(Expr.var(insn.getArray().getIndex()),
|
||||
Expr.var(insn.getIndex().getIndex())), Expr.var(insn.getValue().getIndex()));
|
||||
Expr subscript = Expr.subscript(Expr.var(insn.getArray().getIndex()),
|
||||
Expr.var(insn.getIndex().getIndex()), map(insn.getType()));
|
||||
AssignmentStatement stmt = Statement.assign(subscript, Expr.var(insn.getValue().getIndex()));
|
||||
stmt.setLocation(currentLocation);
|
||||
statements.add(stmt);
|
||||
}
|
||||
|
||||
private static ArrayType map(ArrayElementType type) {
|
||||
switch (type) {
|
||||
case BYTE:
|
||||
return ArrayType.BYTE;
|
||||
case SHORT:
|
||||
return ArrayType.SHORT;
|
||||
case CHAR:
|
||||
return ArrayType.SHORT;
|
||||
case INT:
|
||||
return ArrayType.INT;
|
||||
case LONG:
|
||||
return ArrayType.LONG;
|
||||
case FLOAT:
|
||||
return ArrayType.FLOAT;
|
||||
case DOUBLE:
|
||||
return ArrayType.DOUBLE;
|
||||
case OBJECT:
|
||||
return ArrayType.OBJECT;
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeInstruction insn) {
|
||||
Expr[] exprArgs = new Expr[insn.getMethod().getParameterTypes().length];
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.ArrayType;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.AsyncMethodNode;
|
||||
import org.teavm.ast.AsyncMethodPart;
|
||||
|
@ -509,6 +510,7 @@ public class AstIO {
|
|||
output.writeByte(11);
|
||||
writeExpr(expr.getArray());
|
||||
writeExpr(expr.getIndex());
|
||||
output.writeByte(expr.getType().ordinal());
|
||||
} catch (IOException e) {
|
||||
throw new IOExceptionWrapper(e);
|
||||
}
|
||||
|
@ -899,6 +901,7 @@ public class AstIO {
|
|||
SubscriptExpr expr = new SubscriptExpr();
|
||||
expr.setArray(readExpr(input));
|
||||
expr.setIndex(readExpr(input));
|
||||
expr.setType(ArrayType.values()[input.readByte()]);
|
||||
return expr;
|
||||
}
|
||||
case 12: {
|
||||
|
|
|
@ -536,6 +536,7 @@ public class ProgramIO {
|
|||
public void visit(GetElementInstruction insn) {
|
||||
try {
|
||||
output.writeByte(31);
|
||||
output.writeByte(insn.getType().ordinal());
|
||||
output.writeShort(insn.getReceiver().getIndex());
|
||||
output.writeShort(insn.getArray().getIndex());
|
||||
output.writeShort(insn.getIndex().getIndex());
|
||||
|
@ -548,6 +549,7 @@ public class ProgramIO {
|
|||
public void visit(PutElementInstruction insn) {
|
||||
try {
|
||||
output.writeByte(32);
|
||||
output.writeByte(insn.getType().ordinal());
|
||||
output.writeShort(insn.getArray().getIndex());
|
||||
output.writeShort(insn.getIndex().getIndex());
|
||||
output.writeShort(insn.getValue().getIndex());
|
||||
|
@ -965,14 +967,14 @@ public class ProgramIO {
|
|||
return insn;
|
||||
}
|
||||
case 31: {
|
||||
GetElementInstruction insn = new GetElementInstruction();
|
||||
GetElementInstruction insn = new GetElementInstruction(arrayElementTypes[input.readByte()]);
|
||||
insn.setReceiver(program.variableAt(input.readShort()));
|
||||
insn.setArray(program.variableAt(input.readShort()));
|
||||
insn.setIndex(program.variableAt(input.readShort()));
|
||||
return insn;
|
||||
}
|
||||
case 32: {
|
||||
PutElementInstruction insn = new PutElementInstruction();
|
||||
PutElementInstruction insn = new PutElementInstruction(arrayElementTypes[input.readByte()]);
|
||||
insn.setArray(program.variableAt(input.readShort()));
|
||||
insn.setIndex(program.variableAt(input.readShort()));
|
||||
insn.setValue(program.variableAt(input.readShort()));
|
||||
|
|
|
@ -284,12 +284,13 @@ public class DataFlowGraphBuilder implements InstructionReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
builder.addEdge(array.getIndex(), receiver.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value, ArrayElementType type) {
|
||||
builder.addEdge(value.getIndex(), array.getIndex());
|
||||
}
|
||||
|
||||
|
|
|
@ -655,7 +655,8 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
DependencyNode arrayNode = nodes[array.getIndex()];
|
||||
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
||||
if (arrayNode != null && receiverNode != null && receiverNode != arrayNode.getArrayItem()) {
|
||||
|
@ -664,7 +665,8 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
DependencyNode valueNode = nodes[value.getIndex()];
|
||||
DependencyNode arrayNode = nodes[array.getIndex()];
|
||||
if (valueNode != null && arrayNode != null && valueNode != arrayNode.getArrayItem()) {
|
||||
|
|
|
@ -173,12 +173,12 @@ class InstructionReadVisitor implements InstructionVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(GetElementInstruction insn) {
|
||||
reader.getElement(insn.getReceiver(), insn.getArray(), insn.getIndex());
|
||||
reader.getElement(insn.getReceiver(), insn.getArray(), insn.getIndex(), insn.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PutElementInstruction insn) {
|
||||
reader.putElement(insn.getArray(), insn.getIndex(), insn.getValue());
|
||||
reader.putElement(insn.getArray(), insn.getIndex(), insn.getValue(), insn.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -696,14 +696,16 @@ public class Interpreter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
Object jvmArray = variables[array.getIndex()];
|
||||
int indexValue = (Integer) variables[index.getIndex()];
|
||||
variables[receiver.getIndex()] = Array.get(jvmArray, indexValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
Object jvmArray = variables[array.getIndex()];
|
||||
int indexValue = (Integer) variables[index.getIndex()];
|
||||
Array.set(jvmArray, indexValue, variables[value.getIndex()]);
|
||||
|
|
|
@ -844,7 +844,8 @@ public class ValueEmitter {
|
|||
|
||||
ValueEmitter array = unwrapArray();
|
||||
Variable result = pe.getProgram().createVariable();
|
||||
GetElementInstruction insn = new GetElementInstruction();
|
||||
ValueType.Array arrayType = (ValueType.Array) array.getType();
|
||||
GetElementInstruction insn = new GetElementInstruction(getArrayElementType(arrayType.getItemType()));
|
||||
insn.setArray(array.variable);
|
||||
insn.setIndex(index.widenToInteger().variable);
|
||||
insn.setReceiver(result);
|
||||
|
@ -861,7 +862,7 @@ public class ValueEmitter {
|
|||
throw new EmitException("Can't set element of non-array type: " + type);
|
||||
}
|
||||
|
||||
PutElementInstruction insn = new PutElementInstruction();
|
||||
PutElementInstruction insn = new PutElementInstruction(getArrayElementType(value.getType()));
|
||||
insn.setArray(unwrapArray().variable);
|
||||
insn.setIndex(index.widenToInteger().variable);
|
||||
insn.setValue(value.variable);
|
||||
|
|
|
@ -18,15 +18,20 @@ package org.teavm.model.instructions;
|
|||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.Variable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class GetElementInstruction extends Instruction {
|
||||
private ArrayElementType type;
|
||||
private Variable array;
|
||||
private Variable index;
|
||||
private Variable receiver;
|
||||
|
||||
public GetElementInstruction(ArrayElementType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ArrayElementType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Variable getArray() {
|
||||
return array;
|
||||
}
|
||||
|
|
|
@ -18,10 +18,6 @@ package org.teavm.model.instructions;
|
|||
import java.util.List;
|
||||
import org.teavm.model.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface InstructionReader {
|
||||
void location(InstructionLocation location);
|
||||
|
||||
|
@ -87,9 +83,9 @@ public interface InstructionReader {
|
|||
|
||||
void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType);
|
||||
|
||||
void getElement(VariableReader receiver, VariableReader array, VariableReader index);
|
||||
void getElement(VariableReader receiver, VariableReader array, VariableReader index, ArrayElementType elementType);
|
||||
|
||||
void putElement(VariableReader array, VariableReader index, VariableReader value);
|
||||
void putElement(VariableReader array, VariableReader index, VariableReader value, ArrayElementType elementType);
|
||||
|
||||
void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type);
|
||||
|
|
|
@ -18,15 +18,20 @@ package org.teavm.model.instructions;
|
|||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.Variable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class PutElementInstruction extends Instruction {
|
||||
private ArrayElementType type;
|
||||
private Variable array;
|
||||
private Variable index;
|
||||
private Variable value;
|
||||
|
||||
public PutElementInstruction(ArrayElementType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ArrayElementType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Variable getArray() {
|
||||
return array;
|
||||
}
|
||||
|
|
|
@ -362,11 +362,13 @@ public class AsyncMethodFinder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -386,8 +386,9 @@ public class InstructionCopyReader implements InstructionReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
GetElementInstruction insnCopy = new GetElementInstruction();
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
GetElementInstruction insnCopy = new GetElementInstruction(type);
|
||||
insnCopy.setArray(copyVar(array));
|
||||
insnCopy.setReceiver(copyVar(receiver));
|
||||
insnCopy.setIndex(copyVar(index));
|
||||
|
@ -396,8 +397,8 @@ public class InstructionCopyReader implements InstructionReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
PutElementInstruction insnCopy = new PutElementInstruction();
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value, ArrayElementType type) {
|
||||
PutElementInstruction insnCopy = new PutElementInstruction(type);
|
||||
insnCopy.setArray(copyVar(array));
|
||||
insnCopy.setValue(copyVar(value));
|
||||
insnCopy.setIndex(copyVar(index));
|
||||
|
|
|
@ -307,13 +307,14 @@ public class InstructionStringifier implements InstructionReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
sb.append("@").append(receiver.getIndex()).append(" := @").append(array.getIndex()).append("[@")
|
||||
.append(index.getIndex()).append("]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value, ArrayElementType type) {
|
||||
sb.append("@").append(array.getIndex()).append("[@").append(index.getIndex()).append("] := @")
|
||||
.append(value.getIndex());
|
||||
}
|
||||
|
|
|
@ -234,7 +234,8 @@ public class TypeInferer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -325,7 +326,8 @@ public class TypeInferer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
arrayElemBuilder.addEdge(array.getIndex(), receiver.getIndex());
|
||||
}
|
||||
|
||||
|
|
|
@ -905,7 +905,7 @@ public class ProgramParser {
|
|||
unwrapInsn.setArray(getVariable(array));
|
||||
unwrapInsn.setReceiver(unwrapInsn.getArray());
|
||||
addInstruction(unwrapInsn);
|
||||
GetElementInstruction insn = new GetElementInstruction();
|
||||
GetElementInstruction insn = new GetElementInstruction(type);
|
||||
insn.setArray(getVariable(array));
|
||||
insn.setIndex(getVariable(arrIndex));
|
||||
insn.setReceiver(getVariable(var));
|
||||
|
@ -920,7 +920,7 @@ public class ProgramParser {
|
|||
unwrapInsn.setArray(getVariable(array));
|
||||
unwrapInsn.setReceiver(unwrapInsn.getArray());
|
||||
addInstruction(unwrapInsn);
|
||||
PutElementInstruction insn = new PutElementInstruction();
|
||||
PutElementInstruction insn = new PutElementInstruction(type);
|
||||
insn.setArray(getVariable(array));
|
||||
insn.setIndex(getVariable(arrIndex));
|
||||
insn.setValue(getVariable(value));
|
||||
|
|
|
@ -33,16 +33,14 @@ public final class Allocator {
|
|||
return result;
|
||||
}
|
||||
|
||||
public static Address allocateArray(RuntimeClass tag, int size, byte depth) {
|
||||
public static Address allocateArray(RuntimeClass tag, int size) {
|
||||
Address result = address;
|
||||
int sizeInBytes = (size + 1) * 4 + Structure.sizeOf(RuntimeArray.class);
|
||||
int sizeInBytes = tag.size * 4 + Structure.sizeOf(RuntimeArray.class);
|
||||
address = result.add(sizeInBytes);
|
||||
|
||||
RuntimeArray array = result.toStructure();
|
||||
array.classReference = RuntimeClass.getArrayClass().toAddress().toInt() >> 3;
|
||||
array.componentClassReference = tag.toAddress().toInt() >> 3;
|
||||
array.classReference = tag.toAddress().toInt() >> 3;
|
||||
array.size = size;
|
||||
address.add(Structure.sizeOf(RuntimeArray.class)).putByte(depth);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,5 @@
|
|||
package org.teavm.runtime;
|
||||
|
||||
public class RuntimeArray extends RuntimeObject {
|
||||
public int componentClassReference;
|
||||
int size;
|
||||
}
|
||||
|
|
|
@ -19,20 +19,14 @@ import org.teavm.interop.Structure;
|
|||
|
||||
public class RuntimeClass extends Structure {
|
||||
public static int INITIALIZED = 1;
|
||||
|
||||
public static int BOOLEAN_CLASS = -1;
|
||||
public static int BYTE_CLASS = -2;
|
||||
public static int SHORT_CLASS = -3;
|
||||
public static int CHAR_CLASS = -4;
|
||||
public static int INT_CLASS = -5;
|
||||
public static int LONG_CLASS = -6;
|
||||
public static int FLOAT_CLASS = -7;
|
||||
public static int DOUBLE_CLASS = -8;
|
||||
public static int PRIMITIVE = 1;
|
||||
|
||||
public int size;
|
||||
public int flags;
|
||||
public int tag;
|
||||
public int canary;
|
||||
public RuntimeClass itemType;
|
||||
public RuntimeClass arrayType;
|
||||
|
||||
public static int computeCanary(int size, int tag) {
|
||||
return size ^ (tag << 8) ^ (tag >>> 24) ^ (0xAAAAAAAA);
|
||||
|
@ -41,6 +35,4 @@ public class RuntimeClass extends Structure {
|
|||
public int computeCanary() {
|
||||
return computeCanary(size, tag);
|
||||
}
|
||||
|
||||
public static native RuntimeClass getArrayClass();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.teavm.model.MethodHolder;
|
|||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
@ -62,7 +63,6 @@ import org.teavm.wasm.generate.WasmGenerationContext;
|
|||
import org.teavm.wasm.generate.WasmGenerator;
|
||||
import org.teavm.wasm.generate.WasmMangling;
|
||||
import org.teavm.wasm.intrinsics.WasmAddressIntrinsic;
|
||||
import org.teavm.wasm.intrinsics.WasmRuntimeClassIntrinsic;
|
||||
import org.teavm.wasm.intrinsics.WasmRuntimeIntrinsic;
|
||||
import org.teavm.wasm.intrinsics.WasmStructureIntrinsic;
|
||||
import org.teavm.wasm.model.WasmFunction;
|
||||
|
@ -118,7 +118,7 @@ public class WasmTarget implements TeaVMTarget {
|
|||
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||
RuntimeClass.class, Address.class), null).use();
|
||||
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
RuntimeClass.class, int.class, byte.class, Address.class), null).use();
|
||||
RuntimeClass.class, int.class, Address.class), null).use();
|
||||
|
||||
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use();
|
||||
}
|
||||
|
@ -131,22 +131,14 @@ public class WasmTarget implements TeaVMTarget {
|
|||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
||||
TagRegistry tagRegistry = new TagRegistry(classes);
|
||||
BinaryWriter binaryWriter = new BinaryWriter(256);
|
||||
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, vtableProvider, tagRegistry,
|
||||
binaryWriter);
|
||||
for (String className : classes.getClassNames()) {
|
||||
classGenerator.addClass(className);
|
||||
if (controller.wasCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
classGenerator.addArrayClass();
|
||||
WasmClassGenerator classGenerator = new WasmClassGenerator(controller.getUnprocessedClassSource(),
|
||||
vtableProvider, tagRegistry, binaryWriter);
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>());
|
||||
WasmGenerationContext context = new WasmGenerationContext(classes, vtableProvider, tagRegistry);
|
||||
|
||||
context.addIntrinsic(new WasmAddressIntrinsic());
|
||||
context.addIntrinsic(new WasmRuntimeClassIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new WasmStructureIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new WasmRuntimeIntrinsic());
|
||||
|
||||
|
@ -263,7 +255,7 @@ public class WasmTarget implements TeaVMTarget {
|
|||
|
||||
WasmBlock block = new WasmBlock(false);
|
||||
|
||||
int index = classGenerator.getClassPointer(className);
|
||||
int index = classGenerator.getClassPointer(ValueType.object(className));
|
||||
WasmExpression initFlag = new WasmLoadInt32(4, new WasmInt32Constant(index), WasmInt32Subtype.INT32);
|
||||
initFlag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, initFlag,
|
||||
new WasmInt32Constant(RuntimeClass.INITIALIZED));
|
||||
|
|
|
@ -45,8 +45,7 @@ import org.teavm.wasm.binary.DataValue;
|
|||
|
||||
public class WasmClassGenerator {
|
||||
private ClassReaderSource classSource;
|
||||
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
|
||||
private ClassBinaryData arrayClassData;
|
||||
private Map<ValueType, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
|
||||
private BinaryWriter binaryWriter;
|
||||
private Map<MethodReference, Integer> functions = new HashMap<>();
|
||||
private List<String> functionTable = new ArrayList<>();
|
||||
|
@ -57,7 +56,9 @@ public class WasmClassGenerator {
|
|||
DataPrimitives.INT, /* size */
|
||||
DataPrimitives.INT, /* flags */
|
||||
DataPrimitives.INT, /* tag */
|
||||
DataPrimitives.INT /* canary */);
|
||||
DataPrimitives.INT, /* canary */
|
||||
DataPrimitives.ADDRESS, /* item type */
|
||||
DataPrimitives.ADDRESS /* array type */);
|
||||
|
||||
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider,
|
||||
TagRegistry tagRegistry, BinaryWriter binaryWriter) {
|
||||
|
@ -67,31 +68,69 @@ public class WasmClassGenerator {
|
|||
this.binaryWriter = binaryWriter;
|
||||
}
|
||||
|
||||
public void addClass(String className) {
|
||||
if (binaryDataMap.containsKey(className)) {
|
||||
private void addClass(ValueType type) {
|
||||
if (binaryDataMap.containsKey(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassReader cls = classSource.get(className);
|
||||
ClassBinaryData binaryData = new ClassBinaryData();
|
||||
binaryData.name = className;
|
||||
binaryDataMap.put(className, binaryData);
|
||||
binaryData.type = type;
|
||||
binaryDataMap.put(type, binaryData);
|
||||
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
int size = 0;
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
size = 1;
|
||||
break;
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
size = 2;
|
||||
break;
|
||||
case INTEGER:
|
||||
case FLOAT:
|
||||
size = 4;
|
||||
break;
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
size = 5;
|
||||
break;
|
||||
}
|
||||
|
||||
binaryData.data = createPrimitiveClassData(size);
|
||||
binaryData.start = binaryWriter.append(binaryData.data);
|
||||
} else if (type == ValueType.VOID) {
|
||||
binaryData.data = createPrimitiveClassData(0);
|
||||
binaryData.start = binaryWriter.append(binaryData.data);
|
||||
} else if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
ClassReader cls = classSource.get(className);
|
||||
|
||||
calculateLayout(cls, binaryData);
|
||||
if (binaryData.start < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (binaryData.start >= 0) {
|
||||
binaryData.start = binaryWriter.append(createStructure(binaryData));
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
ValueType itemType = ((ValueType.Array) type).getItemType();
|
||||
addClass(itemType);
|
||||
ClassBinaryData itemBinaryData = binaryDataMap.get(itemType);
|
||||
|
||||
public void addArrayClass() {
|
||||
if (arrayClassData != null) {
|
||||
return;
|
||||
binaryData.size = 4;
|
||||
binaryData.data = classStructure.createValue();
|
||||
binaryData.data.setInt(0, 4);
|
||||
binaryData.data.setAddress(4, itemBinaryData.start);
|
||||
binaryData.start = binaryWriter.append(binaryData.data);
|
||||
|
||||
itemBinaryData.data.setAddress(5, binaryData.start);
|
||||
}
|
||||
}
|
||||
|
||||
arrayClassData = new ClassBinaryData();
|
||||
arrayClassData.start = binaryWriter.append(classStructure.createValue());
|
||||
private DataValue createPrimitiveClassData(int size) {
|
||||
DataValue value = classStructure.createValue();
|
||||
value.setInt(0, size);
|
||||
value.setInt(1, RuntimeClass.PRIMITIVE);
|
||||
return value;
|
||||
}
|
||||
|
||||
public List<String> getFunctionTable() {
|
||||
|
@ -99,16 +138,19 @@ public class WasmClassGenerator {
|
|||
}
|
||||
|
||||
private DataValue createStructure(ClassBinaryData binaryData) {
|
||||
VirtualTable vtable = vtableProvider.lookup(binaryData.name);
|
||||
String name = ((ValueType.Object) binaryData.type).getClassName();
|
||||
|
||||
VirtualTable vtable = vtableProvider.lookup(name);
|
||||
int vtableSize = vtable != null ? vtable.getEntries().size() : 0;
|
||||
|
||||
DataType arrayType = new DataArray(DataPrimitives.INT, vtableSize);
|
||||
DataValue wrapper = new DataStructure((byte) 0, classStructure, arrayType).createValue();
|
||||
DataValue array = wrapper.getValue(1);
|
||||
DataValue header = wrapper.getValue(0);
|
||||
binaryData.data = header;
|
||||
|
||||
header.setInt(0, binaryData.size);
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(binaryData.name);
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(name);
|
||||
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
|
||||
header.setInt(2, tag);
|
||||
header.setInt(3, RuntimeClass.computeCanary(binaryData.size, tag));
|
||||
|
@ -135,27 +177,30 @@ public class WasmClassGenerator {
|
|||
return wrapper;
|
||||
}
|
||||
|
||||
public int getClassPointer(String className) {
|
||||
ClassBinaryData data = binaryDataMap.get(className);
|
||||
public int getClassPointer(ValueType type) {
|
||||
addClass(type);
|
||||
ClassBinaryData data = binaryDataMap.get(type);
|
||||
return data.start;
|
||||
}
|
||||
|
||||
public int getFieldOffset(FieldReference field) {
|
||||
ClassBinaryData data = binaryDataMap.get(field.getClassName());
|
||||
ValueType type = ValueType.object(field.getClassName());
|
||||
addClass(type);
|
||||
ClassBinaryData data = binaryDataMap.get(type);
|
||||
return data.fieldLayout.get(field.getFieldName());
|
||||
}
|
||||
|
||||
public int getClassSize(String className) {
|
||||
ClassBinaryData data = binaryDataMap.get(className);
|
||||
ValueType type = ValueType.object(className);
|
||||
addClass(type);
|
||||
ClassBinaryData data = binaryDataMap.get(type);
|
||||
return data.size;
|
||||
}
|
||||
|
||||
public int getArrayClassPointer() {
|
||||
return arrayClassData.start;
|
||||
}
|
||||
|
||||
public boolean isStructure(String className) {
|
||||
ClassBinaryData data = binaryDataMap.get(className);
|
||||
ValueType type = ValueType.object(className);
|
||||
addClass(type);
|
||||
ClassBinaryData data = binaryDataMap.get(type);
|
||||
return data.start < 0;
|
||||
}
|
||||
|
||||
|
@ -164,8 +209,8 @@ public class WasmClassGenerator {
|
|||
data.size = 0;
|
||||
data.start = -1;
|
||||
} else if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
|
||||
addClass(cls.getParent());
|
||||
ClassBinaryData parentData = binaryDataMap.get(cls.getParent());
|
||||
addClass(ValueType.object(cls.getParent()));
|
||||
ClassBinaryData parentData = binaryDataMap.get(ValueType.object(cls.getParent()));
|
||||
data.size = parentData.size;
|
||||
if (parentData.start == -1) {
|
||||
data.start = -1;
|
||||
|
@ -237,9 +282,10 @@ public class WasmClassGenerator {
|
|||
}
|
||||
|
||||
private class ClassBinaryData {
|
||||
String name;
|
||||
ValueType type;
|
||||
int size;
|
||||
int start;
|
||||
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>();
|
||||
DataValue data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -528,7 +528,6 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
indexExpr.acceptVisitor(this);
|
||||
WasmExpression index = result;
|
||||
|
||||
classGenerator.addClass(RuntimeArray.class.getName());
|
||||
int base = classGenerator.getClassSize(RuntimeArray.class.getName());
|
||||
array = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, array, new WasmInt32Constant(base));
|
||||
index = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, index, new WasmInt32Constant(2));
|
||||
|
@ -673,7 +672,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod());
|
||||
WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||
getReferenceToClass(instance), new WasmInt32Constant(vtableEntry.getIndex() * 4 + 16));
|
||||
getReferenceToClass(instance), new WasmInt32Constant(vtableEntry.getIndex() * 4 + 24));
|
||||
methodIndex = new WasmLoadInt32(4, methodIndex, WasmInt32Subtype.INT32);
|
||||
|
||||
WasmIndirectCall call = new WasmIndirectCall(methodIndex);
|
||||
|
@ -788,7 +787,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(NewExpr expr) {
|
||||
int tag = classGenerator.getClassPointer(expr.getConstructedClass());
|
||||
int tag = classGenerator.getClassPointer(ValueType.object(expr.getConstructedClass()));
|
||||
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocate",
|
||||
RuntimeClass.class, Address.class));
|
||||
WasmCall call = new WasmCall(allocName);
|
||||
|
@ -799,21 +798,14 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
@Override
|
||||
public void visit(NewArrayExpr expr) {
|
||||
ValueType type = expr.getType();
|
||||
int depth = 0;
|
||||
while (type instanceof ValueType.Array) {
|
||||
++depth;
|
||||
type = ((ValueType.Array) type).getItemType();
|
||||
}
|
||||
|
||||
ValueType.Object cls = (ValueType.Object) type;
|
||||
int classPointer = classGenerator.getClassPointer(cls.getClassName());
|
||||
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type));
|
||||
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
RuntimeClass.class, int.class, byte.class, Address.class));
|
||||
RuntimeClass.class, int.class, Address.class));
|
||||
WasmCall call = new WasmCall(allocName);
|
||||
call.getArguments().add(new WasmInt32Constant(classPointer));
|
||||
expr.getLength().acceptVisitor(this);
|
||||
call.getArguments().add(result);
|
||||
call.getArguments().add(new WasmInt32Constant(depth));
|
||||
|
||||
result = call;
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* 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.teavm.wasm.intrinsics;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.wasm.generate.WasmClassGenerator;
|
||||
import org.teavm.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.wasm.model.expression.WasmInt32Constant;
|
||||
|
||||
public class WasmRuntimeClassIntrinsic implements WasmIntrinsic {
|
||||
private WasmClassGenerator classGenerator;
|
||||
|
||||
public WasmRuntimeClassIntrinsic(WasmClassGenerator classGenerator) {
|
||||
this.classGenerator = classGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
if (!methodReference.getClassName().equals(RuntimeClass.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (methodReference.getName()) {
|
||||
case "getArrayClass":
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getArrayClass":
|
||||
return new WasmInt32Constant(classGenerator.getArrayClassPointer());
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ public class WasmStructureIntrinsic implements WasmIntrinsic {
|
|||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
return !methodReference.getClassName().equals(Address.class.getName())
|
||||
&& classGenerator.getClassPointer(methodReference.getClassName()) < 0;
|
||||
&& classGenerator.getClassPointer(ValueType.object(methodReference.getClassName())) < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -266,7 +266,8 @@ class AliasFinder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
ArrayElement elem = new ArrayElement();
|
||||
elem.array = array.getIndex();
|
||||
elem.index = index.getIndex();
|
||||
|
@ -274,7 +275,8 @@ class AliasFinder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -768,7 +768,8 @@ public class CompositeMethodGenerator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
int arrayIndex = variableMapping[array.getIndex()];
|
||||
|
||||
AliasFinder.ArrayElement elem = arrayElements[receiver.getIndex()];
|
||||
|
@ -780,7 +781,7 @@ public class CompositeMethodGenerator {
|
|||
return;
|
||||
}
|
||||
|
||||
GetElementInstruction insn = new GetElementInstruction();
|
||||
GetElementInstruction insn = new GetElementInstruction(type);
|
||||
insn.setArray(var(array));
|
||||
insn.setIndex(var(index));
|
||||
insn.setReceiver(var(receiver));
|
||||
|
@ -788,8 +789,9 @@ public class CompositeMethodGenerator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
PutElementInstruction insn = new PutElementInstruction();
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
PutElementInstruction insn = new PutElementInstruction(type);
|
||||
insn.setArray(var(array));
|
||||
insn.setIndex(var(index));
|
||||
insn.setValue(var(value));
|
||||
|
@ -1013,7 +1015,7 @@ public class CompositeMethodGenerator {
|
|||
return true;
|
||||
}
|
||||
case "getArrayElement": {
|
||||
GetElementInstruction insn = new GetElementInstruction();
|
||||
GetElementInstruction insn = new GetElementInstruction(asArrayType(reflectClass.type));
|
||||
insn.setArray(unwrapArray(reflectClass.type, var(arguments.get(0))));
|
||||
insn.setIndex(var(arguments.get(1)));
|
||||
insn.setReceiver(program.createVariable());
|
||||
|
@ -1047,7 +1049,7 @@ public class CompositeMethodGenerator {
|
|||
indexInsn.setReceiver(program.createVariable());
|
||||
add(indexInsn);
|
||||
|
||||
GetElementInstruction extractArgInsn = new GetElementInstruction();
|
||||
GetElementInstruction extractArgInsn = new GetElementInstruction(ArrayElementType.OBJECT);
|
||||
extractArgInsn.setArray(argumentsVar);
|
||||
extractArgInsn.setIndex(indexInsn.getReceiver());
|
||||
extractArgInsn.setReceiver(program.createVariable());
|
||||
|
|
|
@ -162,11 +162,13 @@ class InstructionLocationReader implements InstructionReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
|
||||
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value) {
|
||||
public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,10 +20,6 @@ import java.util.Set;
|
|||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class ProgramSourceAggregator implements InstructionReader {
|
||||
private Set<String> sourceFiles;
|
||||
|
||||
|
@ -82,8 +78,10 @@ class ProgramSourceAggregator implements InstructionReader {
|
|||
@Override public void arrayLength(VariableReader receiver, VariableReader array) { }
|
||||
@Override public void cloneArray(VariableReader receiver, VariableReader array) { }
|
||||
@Override public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) { }
|
||||
@Override public void getElement(VariableReader receiver, VariableReader array, VariableReader index) { }
|
||||
@Override public void putElement(VariableReader array, VariableReader index, VariableReader value) { }
|
||||
@Override public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
|
||||
ArrayElementType type) { }
|
||||
@Override public void putElement(VariableReader array, VariableReader index, VariableReader value,
|
||||
ArrayElementType type) { }
|
||||
@Override public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) { }
|
||||
@Override public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
|
|
Loading…
Reference in New Issue
Block a user