mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
First working prototype of JS to Java call with dependency checking and
type validation
This commit is contained in:
parent
6986f1c02a
commit
1e05cb42ea
|
@ -30,4 +30,7 @@ class JSBodyRepository {
|
||||||
public final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
|
public final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
|
||||||
public final Set<MethodReference> processedMethods = new HashSet<>();
|
public final Set<MethodReference> processedMethods = new HashSet<>();
|
||||||
public final Set<MethodReference> inlineMethods = new HashSet<>();
|
public final Set<MethodReference> inlineMethods = new HashSet<>();
|
||||||
|
public final Map<MethodReference, MethodReference> callbackCallees = new HashMap<>();
|
||||||
|
public final Map<MethodReference, Set<MethodReference>> callbackMethods = new HashMap<>();
|
||||||
|
public final Map<MethodReference, Set<MethodReference>> callbackMethodsDeps = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
import org.teavm.model.instructions.CastInstruction;
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.StringConstantInstruction;
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
@ -80,6 +81,8 @@ import org.teavm.model.util.ProgramUtils;
|
||||||
*/
|
*/
|
||||||
class JSClassProcessor {
|
class JSClassProcessor {
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
|
private JSBodyRepository repository;
|
||||||
|
private JavaInvocationProcessor javaInvocationProcessor;
|
||||||
private Program program;
|
private Program program;
|
||||||
private List<Instruction> replacement = new ArrayList<>();
|
private List<Instruction> replacement = new ArrayList<>();
|
||||||
private JSTypeHelper typeHelper;
|
private JSTypeHelper typeHelper;
|
||||||
|
@ -87,9 +90,12 @@ class JSClassProcessor {
|
||||||
private int methodIndexGenerator;
|
private int methodIndexGenerator;
|
||||||
private Map<MethodReference, MethodReader> overridenMethodCache = new HashMap<>();
|
private Map<MethodReference, MethodReader> overridenMethodCache = new HashMap<>();
|
||||||
|
|
||||||
public JSClassProcessor(ClassReaderSource classSource) {
|
public JSClassProcessor(ClassReaderSource classSource, JSBodyRepository repository, Diagnostics diagnostics) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
this.repository = repository;
|
||||||
|
this.diagnostics = diagnostics;
|
||||||
typeHelper = new JSTypeHelper(classSource);
|
typeHelper = new JSTypeHelper(classSource);
|
||||||
|
javaInvocationProcessor = new JavaInvocationProcessor(typeHelper, repository, classSource, diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassReaderSource getClassSource() {
|
public ClassReaderSource getClassSource() {
|
||||||
|
@ -104,10 +110,6 @@ class JSClassProcessor {
|
||||||
return typeHelper.isJavaScriptImplementation(className);
|
return typeHelper.isJavaScriptImplementation(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDiagnostics(Diagnostics diagnostics) {
|
|
||||||
this.diagnostics = diagnostics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MethodReference isFunctor(String className) {
|
public MethodReference isFunctor(String className) {
|
||||||
if (!typeHelper.isJavaScriptImplementation(className)) {
|
if (!typeHelper.isJavaScriptImplementation(className)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -281,7 +283,7 @@ class JSClassProcessor {
|
||||||
return staticSignature;
|
return staticSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processProgram(JSBodyRepository repository, MethodHolder methodToProcess) {
|
public void processProgram(MethodHolder methodToProcess) {
|
||||||
program = methodToProcess.getProgram();
|
program = methodToProcess.getProgram();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
@ -299,7 +301,7 @@ class JSClassProcessor {
|
||||||
}
|
}
|
||||||
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
||||||
replacement.clear();
|
replacement.clear();
|
||||||
if (processInvocation(repository, method, callLocation, invoke)) {
|
if (processInvocation(method, callLocation, invoke)) {
|
||||||
block.getInstructions().set(j, replacement.get(0));
|
block.getInstructions().set(j, replacement.get(0));
|
||||||
block.getInstructions().addAll(j + 1, replacement.subList(1, replacement.size()));
|
block.getInstructions().addAll(j + 1, replacement.subList(1, replacement.size()));
|
||||||
j += replacement.size() - 1;
|
j += replacement.size() - 1;
|
||||||
|
@ -308,10 +310,9 @@ class JSClassProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processInvocation(JSBodyRepository repository, MethodReader method, CallLocation callLocation,
|
private boolean processInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
|
||||||
InvokeInstruction invoke) {
|
|
||||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||||
return processJSBodyInvocation(repository, method, callLocation, invoke);
|
return processJSBodyInvocation(method, callLocation, invoke);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!typeHelper.isJavaScriptClass(invoke.getMethod().getClassName())) {
|
if (!typeHelper.isJavaScriptClass(invoke.getMethod().getClassName())) {
|
||||||
|
@ -348,9 +349,8 @@ class JSClassProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processJSBodyInvocation(JSBodyRepository repository, MethodReader method,
|
private boolean processJSBodyInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
|
||||||
CallLocation callLocation, InvokeInstruction invoke) {
|
requireJSBody(diagnostics, method);
|
||||||
requireJSBody(repository, diagnostics, method);
|
|
||||||
MethodReference delegate = repository.methodMap.get(method.getReference());
|
MethodReference delegate = repository.methodMap.get(method.getReference());
|
||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -491,14 +491,14 @@ class JSClassProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requireJSBody(JSBodyRepository repository, Diagnostics diagnostics, MethodReader methodToProcess) {
|
private void requireJSBody(Diagnostics diagnostics, MethodReader methodToProcess) {
|
||||||
if (!repository.processedMethods.add(methodToProcess.getReference())) {
|
if (!repository.processedMethods.add(methodToProcess.getReference())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processJSBody(repository, diagnostics, methodToProcess);
|
processJSBody(diagnostics, methodToProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processJSBody(JSBodyRepository repository, Diagnostics diagnostics, MethodReader methodToProcess) {
|
private void processJSBody(Diagnostics diagnostics, MethodReader methodToProcess) {
|
||||||
CallLocation location = new CallLocation(methodToProcess.getReference());
|
CallLocation location = new CallLocation(methodToProcess.getReference());
|
||||||
boolean isStatic = methodToProcess.hasModifier(ElementModifier.STATIC);
|
boolean isStatic = methodToProcess.hasModifier(ElementModifier.STATIC);
|
||||||
|
|
||||||
|
@ -569,6 +569,7 @@ class JSClassProcessor {
|
||||||
}
|
}
|
||||||
parser.exitFunction();
|
parser.exitFunction();
|
||||||
|
|
||||||
|
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
||||||
if (errorReporter.hasErrors()) {
|
if (errorReporter.hasErrors()) {
|
||||||
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
||||||
script, parameterNames));
|
script, parameterNames));
|
||||||
|
@ -580,36 +581,87 @@ class JSClassProcessor {
|
||||||
} else {
|
} else {
|
||||||
expr = rootNode;
|
expr = rootNode;
|
||||||
}
|
}
|
||||||
JavaInvocationProcessor javaInvocationProcessor = new JavaInvocationProcessor(typeHelper,
|
javaInvocationProcessor.process(location, expr);
|
||||||
classSource, diagnostics);
|
|
||||||
javaInvocationProcessor.validate(location, expr);
|
|
||||||
repository.emitters.put(proxyMethod, new JSBodyAstEmitter(isStatic, expr, parameterNames));
|
repository.emitters.put(proxyMethod, new JSBodyAstEmitter(isStatic, expr, parameterNames));
|
||||||
}
|
}
|
||||||
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createJSMethods(JSBodyRepository repository, ClassHolder cls) {
|
public void createJSMethods(ClassHolder cls) {
|
||||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||||
MethodReference methodRef = method.getReference();
|
MethodReference methodRef = method.getReference();
|
||||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
if (method.getAnnotations().get(JSBody.class.getName()) == null) {
|
||||||
requireJSBody(repository, diagnostics, method);
|
continue;
|
||||||
if (repository.methodMap.containsKey(method.getReference())) {
|
}
|
||||||
MethodReference proxyRef = repository.methodMap.get(methodRef);
|
|
||||||
MethodHolder proxyMethod = new MethodHolder(proxyRef.getDescriptor());
|
requireJSBody(diagnostics, method);
|
||||||
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
if (!repository.methodMap.containsKey(method.getReference())) {
|
||||||
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
continue;
|
||||||
boolean inline = repository.inlineMethods.contains(methodRef);
|
}
|
||||||
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
|
||||||
? InjectedBy.class.getName() : GeneratedBy.class.getName());
|
MethodReference proxyRef = repository.methodMap.get(methodRef);
|
||||||
generatorAnnot.getValues().put("value",
|
MethodHolder proxyMethod = new MethodHolder(proxyRef.getDescriptor());
|
||||||
new AnnotationValue(ValueType.parse(JSBodyGenerator.class)));
|
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||||
proxyMethod.getAnnotations().add(generatorAnnot);
|
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
||||||
cls.addMethod(proxyMethod);
|
boolean inline = repository.inlineMethods.contains(methodRef);
|
||||||
|
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
||||||
|
? InjectedBy.class.getName() : GeneratedBy.class.getName());
|
||||||
|
generatorAnnot.getValues().put("value",
|
||||||
|
new AnnotationValue(ValueType.parse(JSBodyGenerator.class)));
|
||||||
|
proxyMethod.getAnnotations().add(generatorAnnot);
|
||||||
|
cls.addMethod(proxyMethod);
|
||||||
|
|
||||||
|
Set<MethodReference> callbacks = repository.callbackMethods.get(proxyRef);
|
||||||
|
if (callbacks != null) {
|
||||||
|
for (MethodReference callback : callbacks) {
|
||||||
|
generateCallbackCaller(cls, callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateCallbackCaller(ClassHolder cls, MethodReference callback) {
|
||||||
|
MethodReference calleeRef = repository.callbackCallees.get(callback);
|
||||||
|
MethodReader callee = classSource.resolve(calleeRef);
|
||||||
|
MethodHolder callerMethod = new MethodHolder(callback.getDescriptor());
|
||||||
|
callerMethod.getModifiers().add(ElementModifier.STATIC);
|
||||||
|
CallLocation location = new CallLocation(callback);
|
||||||
|
|
||||||
|
program = new Program();
|
||||||
|
for (int i = 0; i <= callback.parameterCount(); ++i) {
|
||||||
|
program.createVariable();
|
||||||
|
}
|
||||||
|
BasicBlock block = program.createBasicBlock();
|
||||||
|
|
||||||
|
int paramIndex = 1;
|
||||||
|
InvokeInstruction insn = new InvokeInstruction();
|
||||||
|
insn.setType(InvocationType.SPECIAL);
|
||||||
|
insn.setMethod(calleeRef);
|
||||||
|
replacement.clear();
|
||||||
|
if (!callee.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
insn.setInstance(unwrap(location, program.variableAt(paramIndex++),
|
||||||
|
ValueType.object(calleeRef.getClassName())));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < callee.parameterCount(); ++i) {
|
||||||
|
insn.getArguments().add(unwrap(location, program.variableAt(paramIndex++), callee.parameterType(i)));
|
||||||
|
}
|
||||||
|
if (callee.getResultType() != ValueType.VOID) {
|
||||||
|
insn.setReceiver(program.createVariable());
|
||||||
|
}
|
||||||
|
block.getInstructions().addAll(replacement);
|
||||||
|
block.getInstructions().add(insn);
|
||||||
|
|
||||||
|
ExitInstruction exit = new ExitInstruction();
|
||||||
|
if (insn.getReceiver() != null) {
|
||||||
|
replacement.clear();
|
||||||
|
exit.setValueToReturn(wrap(insn.getReceiver(), callee.getResultType(), null));
|
||||||
|
block.getInstructions().addAll(replacement);
|
||||||
|
}
|
||||||
|
block.getInstructions().add(exit);
|
||||||
|
|
||||||
|
callerMethod.setProgram(program);
|
||||||
|
cls.addMethod(callerMethod);
|
||||||
|
}
|
||||||
|
|
||||||
private void addPropertyGet(String propertyName, Variable instance, Variable receiver,
|
private void addPropertyGet(String propertyName, Variable instance, Variable receiver,
|
||||||
InstructionLocation location) {
|
InstructionLocation location) {
|
||||||
Variable nameVar = addStringWrap(addString(propertyName, location), location);
|
Variable nameVar = addStringWrap(addString(propertyName, location), location);
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -42,14 +43,30 @@ class JSDependencyListener extends AbstractDependencyListener {
|
||||||
private Map<String, ExposedClass> exposedClasses = new HashMap<>();
|
private Map<String, ExposedClass> exposedClasses = new HashMap<>();
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
private DependencyAgent agent;
|
private DependencyAgent agent;
|
||||||
|
private JSBodyRepository repository;
|
||||||
private boolean anyAliasExists;
|
private boolean anyAliasExists;
|
||||||
|
|
||||||
|
public JSDependencyListener(JSBodyRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void started(DependencyAgent agent) {
|
public void started(DependencyAgent agent) {
|
||||||
this.agent = agent;
|
this.agent = agent;
|
||||||
classSource = agent.getClassSource();
|
classSource = agent.getClassSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||||
|
MethodReference ref = method.getReference();
|
||||||
|
Set<MethodReference> callbackMethods = repository.callbackMethods.get(ref);
|
||||||
|
if (callbackMethods != null) {
|
||||||
|
for (MethodReference callbackMethod : callbackMethods) {
|
||||||
|
agent.linkMethod(callbackMethod, new CallLocation(ref)).use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
public void classReached(DependencyAgent agent, String className, CallLocation location) {
|
||||||
getExposedClass(className);
|
getExposedClass(className);
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||||
JSBodyRepository repository = new JSBodyRepository();
|
JSBodyRepository repository = new JSBodyRepository();
|
||||||
host.registerService(JSBodyRepository.class, repository);
|
host.registerService(JSBodyRepository.class, repository);
|
||||||
host.add(new JSObjectClassTransformer(repository));
|
host.add(new JSObjectClassTransformer(repository));
|
||||||
JSDependencyListener dependencyListener = new JSDependencyListener();
|
JSDependencyListener dependencyListener = new JSDependencyListener(repository);
|
||||||
JSAliasRenderer aliasRenderer = new JSAliasRenderer(dependencyListener);
|
JSAliasRenderer aliasRenderer = new JSAliasRenderer(dependencyListener);
|
||||||
host.add(dependencyListener);
|
host.add(dependencyListener);
|
||||||
host.add(aliasRenderer);
|
host.add(aliasRenderer);
|
||||||
|
|
|
@ -37,9 +37,8 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||||
if (processor == null || processor.getClassSource() != innerSource) {
|
if (processor == null || processor.getClassSource() != innerSource) {
|
||||||
processor = new JSClassProcessor(innerSource);
|
processor = new JSClassProcessor(innerSource, repository, diagnostics);
|
||||||
}
|
}
|
||||||
processor.setDiagnostics(diagnostics);
|
|
||||||
processor.processClass(cls);
|
processor.processClass(cls);
|
||||||
if (processor.isNative(cls.getName())) {
|
if (processor.isNative(cls.getName())) {
|
||||||
processor.processFinalMethods(cls);
|
processor.processFinalMethods(cls);
|
||||||
|
@ -55,9 +54,9 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||||
if (method.getProgram() != null) {
|
if (method.getProgram() != null) {
|
||||||
processor.processProgram(repository, method);
|
processor.processProgram(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processor.createJSMethods(repository, cls);
|
processor.createJSMethods(cls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.jso.impl;
|
package org.teavm.jso.impl;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import org.mozilla.javascript.Token;
|
import org.mozilla.javascript.Token;
|
||||||
import org.mozilla.javascript.ast.AstNode;
|
import org.mozilla.javascript.ast.AstNode;
|
||||||
import org.mozilla.javascript.ast.FunctionCall;
|
import org.mozilla.javascript.ast.FunctionCall;
|
||||||
|
@ -24,11 +25,13 @@ import org.mozilla.javascript.ast.NodeVisitor;
|
||||||
import org.mozilla.javascript.ast.PropertyGet;
|
import org.mozilla.javascript.ast.PropertyGet;
|
||||||
import org.mozilla.javascript.ast.StringLiteral;
|
import org.mozilla.javascript.ast.StringLiteral;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -37,16 +40,20 @@ import org.teavm.model.MethodReference;
|
||||||
class JavaInvocationProcessor implements NodeVisitor {
|
class JavaInvocationProcessor implements NodeVisitor {
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
private JSTypeHelper typeHelper;
|
private JSTypeHelper typeHelper;
|
||||||
|
private JSBodyRepository repository;
|
||||||
private Diagnostics diagnostics;
|
private Diagnostics diagnostics;
|
||||||
private CallLocation location;
|
private CallLocation location;
|
||||||
|
private int idGenerator;
|
||||||
|
|
||||||
public JavaInvocationProcessor(JSTypeHelper typeHelper, ClassReaderSource classSource, Diagnostics diagnostics) {
|
public JavaInvocationProcessor(JSTypeHelper typeHelper, JSBodyRepository repository,
|
||||||
|
ClassReaderSource classSource, Diagnostics diagnostics) {
|
||||||
this.typeHelper = typeHelper;
|
this.typeHelper = typeHelper;
|
||||||
|
this.repository = repository;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(CallLocation location, AstNode root) {
|
public void process(CallLocation location, AstNode root) {
|
||||||
this.location = location;
|
this.location = location;
|
||||||
root.visit(this);
|
root.visit(this);
|
||||||
}
|
}
|
||||||
|
@ -54,12 +61,12 @@ class JavaInvocationProcessor implements NodeVisitor {
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(AstNode node) {
|
public boolean visit(AstNode node) {
|
||||||
if (node instanceof FunctionCall) {
|
if (node instanceof FunctionCall) {
|
||||||
return validateCall((FunctionCall) node);
|
return visit((FunctionCall) node);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateCall(FunctionCall call) {
|
private boolean visit(FunctionCall call) {
|
||||||
if (!(call.getTarget() instanceof PropertyGet)) {
|
if (!(call.getTarget() instanceof PropertyGet)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -89,9 +96,15 @@ class JavaInvocationProcessor implements NodeVisitor {
|
||||||
+ ", encountered: " + call.getArguments().size(), methodRef);
|
+ ", encountered: " + call.getArguments().size(), methodRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MethodReference caller = createCallbackMethod(method);
|
||||||
|
MethodReference delegate = repository.methodMap.get(location.getMethod());
|
||||||
|
repository.callbackCallees.put(caller, methodRef);
|
||||||
|
repository.callbackMethods.computeIfAbsent(delegate, key -> new HashSet<>()).add(caller);
|
||||||
|
validateSignature(method);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("$$JSO$$_");
|
StringBuilder sb = new StringBuilder("$$JSO$$_");
|
||||||
sb.append(method.hasModifier(ElementModifier.STATIC) ? 'S' : "V");
|
sb.append(method.hasModifier(ElementModifier.STATIC) ? 'S' : "V");
|
||||||
sb.append(method.getReference().toString());
|
sb.append(caller.toString());
|
||||||
StringLiteral newTarget = new StringLiteral();
|
StringLiteral newTarget = new StringLiteral();
|
||||||
newTarget.setValue(sb.toString());
|
newTarget.setValue(sb.toString());
|
||||||
propertyGet.setTarget(newTarget);
|
propertyGet.setTarget(newTarget);
|
||||||
|
@ -99,6 +112,39 @@ class JavaInvocationProcessor implements NodeVisitor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateSignature(MethodReader method) {
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
if (!typeHelper.isJavaScriptClass(method.getOwnerName())) {
|
||||||
|
diagnostics.error(location, "Can't call method {{m0}} of non-JS class", method.getReference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
|
if (!typeHelper.isSupportedType(method.parameterType(i))) {
|
||||||
|
diagnostics.error(location, "Invalid type {{t0}} of parameter " + (i + 1) + " of method {{m1}}",
|
||||||
|
method.parameterType(i), method.getReference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method.getResultType() != ValueType.VOID && !typeHelper.isSupportedType(method.getResultType())) {
|
||||||
|
diagnostics.error(location, "Invalid type {{t0}} of return value of method {{m1}}", method.getResultType(),
|
||||||
|
method.getReference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodReference createCallbackMethod(MethodReader method) {
|
||||||
|
int paramCount = method.parameterCount();
|
||||||
|
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
paramCount++;
|
||||||
|
}
|
||||||
|
ValueType[] signature = new ValueType[paramCount + 1];
|
||||||
|
for (int i = 0; i < paramCount; ++i) {
|
||||||
|
signature[i] = ValueType.object(JSObject.class.getName());
|
||||||
|
}
|
||||||
|
signature[paramCount] = method.getResultType() == ValueType.VOID ? ValueType.VOID
|
||||||
|
: ValueType.object(JSObject.class.getName());
|
||||||
|
return new MethodReference(location.getMethod().getClassName(),
|
||||||
|
method.getName() + "$jsocb$_" + idGenerator++, signature);
|
||||||
|
}
|
||||||
|
|
||||||
private MethodReference getJavaMethodSelector(AstNode node) {
|
private MethodReference getJavaMethodSelector(AstNode node) {
|
||||||
if (!(node instanceof FunctionCall)) {
|
if (!(node instanceof FunctionCall)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user