mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Improve JSBody implementation
This commit is contained in:
parent
d4309bb564
commit
49f05cbb6e
|
@ -656,6 +656,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
|
|||
}
|
||||
writer.append(";").softNewLine();
|
||||
}
|
||||
|
||||
end = true;
|
||||
currentPart = 0;
|
||||
method.getBody().acceptVisitor(Renderer.this);
|
||||
|
|
|
@ -95,7 +95,7 @@ public class AstWriter {
|
|||
private static final int PRECEDENCE_ASSIGN = 17;
|
||||
private static final int PRECEDENCE_COMMA = 18;
|
||||
private SourceWriter writer;
|
||||
private Map<String, String> nameMap = new HashMap<>();
|
||||
private Map<String, NameEmitter> nameMap = new HashMap<>();
|
||||
private Set<String> aliases = new HashSet<>();
|
||||
|
||||
public AstWriter(SourceWriter writer) {
|
||||
|
@ -107,13 +107,13 @@ public class AstWriter {
|
|||
return;
|
||||
}
|
||||
if (aliases.add(name)) {
|
||||
nameMap.put(name, name);
|
||||
nameMap.put(name, () -> writer.append(name));
|
||||
return;
|
||||
}
|
||||
for (int i = 0;; ++i) {
|
||||
String alias = name + "_" + i;
|
||||
if (aliases.add(alias)) {
|
||||
nameMap.put(name, alias);
|
||||
nameMap.put(name, () -> writer.append(alias));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,16 @@ public class AstWriter {
|
|||
if (!aliases.add(alias)) {
|
||||
throw new IllegalArgumentException("Alias " + alias + " is already occupied");
|
||||
}
|
||||
nameMap.put(name, alias);
|
||||
nameMap.put(name, () -> writer.append(alias));
|
||||
}
|
||||
|
||||
public void reserveName(String name) {
|
||||
aliases.add(name);
|
||||
nameMap.put(name, () -> writer.append(name));
|
||||
}
|
||||
|
||||
public void declareNameEmitter(String name, NameEmitter emitter) {
|
||||
nameMap.put(name, emitter);
|
||||
}
|
||||
|
||||
public void hoist(AstNode node) {
|
||||
|
@ -181,7 +190,11 @@ public class AstWriter {
|
|||
writer.append("false");
|
||||
break;
|
||||
case Token.THIS:
|
||||
writer.append(nameMap.containsKey("this") ? nameMap.get("this") : "this");
|
||||
if (nameMap.containsKey("this")) {
|
||||
nameMap.get("this").emit();
|
||||
} else {
|
||||
writer.append("this");
|
||||
}
|
||||
break;
|
||||
case Token.NULL:
|
||||
writer.append("null");
|
||||
|
@ -481,7 +494,7 @@ public class AstWriter {
|
|||
private void print(PropertyGet node) throws IOException {
|
||||
print(node.getLeft(), PRECEDENCE_MEMBER);
|
||||
writer.append('.');
|
||||
Map<String, String> oldNameMap = nameMap;
|
||||
Map<String, NameEmitter> oldNameMap = nameMap;
|
||||
nameMap = Collections.emptyMap();
|
||||
print(node.getRight());
|
||||
nameMap = oldNameMap;
|
||||
|
@ -583,11 +596,11 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
private void print(Name node) throws IOException {
|
||||
String alias = nameMap.get(node.getIdentifier());
|
||||
NameEmitter alias = nameMap.get(node.getIdentifier());
|
||||
if (alias == null) {
|
||||
alias = node.getIdentifier();
|
||||
alias = () -> writer.append(node.getIdentifier());
|
||||
}
|
||||
writer.append(alias);
|
||||
alias.emit();
|
||||
}
|
||||
|
||||
private void print(RegExpLiteral node) throws IOException {
|
||||
|
@ -618,7 +631,7 @@ public class AstWriter {
|
|||
} else if (node.isSetterMethod()) {
|
||||
writer.append("set ");
|
||||
}
|
||||
Map<String, String> oldNameMap = nameMap;
|
||||
Map<String, NameEmitter> oldNameMap = nameMap;
|
||||
nameMap = Collections.emptyMap();
|
||||
print(node.getLeft());
|
||||
nameMap = oldNameMap;
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2015 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.jso.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.javascript.spi.GeneratorContext;
|
||||
import org.teavm.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class JSBodyAstEmitter implements JSBodyEmitter {
|
||||
private boolean isStatic;
|
||||
private AstRoot ast;
|
||||
private String[] parameterNames;
|
||||
|
||||
public JSBodyAstEmitter(boolean isStatic, AstRoot ast, String[] parameterNames) {
|
||||
this.isStatic = isStatic;
|
||||
this.ast = ast;
|
||||
this.parameterNames = parameterNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(InjectorContext context) throws IOException {
|
||||
AstWriter astWriter = new AstWriter(context.getWriter());
|
||||
int paramIndex = 0;
|
||||
if (!isStatic) {
|
||||
int index = paramIndex++;
|
||||
astWriter.declareNameEmitter("this", () -> context.writeExpr(context.getArgument(index)));
|
||||
}
|
||||
for (int i = 0; i < parameterNames.length; ++i) {
|
||||
int index = paramIndex++;
|
||||
astWriter.declareNameEmitter(parameterNames[i], () -> context.writeExpr(context.getArgument(index)));
|
||||
}
|
||||
astWriter.hoist(ast);
|
||||
astWriter.print(ast);
|
||||
context.getWriter().softNewLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
AstWriter astWriter = new AstWriter(writer);
|
||||
int paramIndex = 1;
|
||||
if (!isStatic) {
|
||||
int index = paramIndex++;
|
||||
astWriter.declareNameEmitter("this", () -> writer.append(context.getParameterName(index)));
|
||||
}
|
||||
for (int i = 0; i < parameterNames.length; ++i) {
|
||||
int index = paramIndex++;
|
||||
astWriter.declareNameEmitter(parameterNames[i], () -> writer.append(context.getParameterName(index)));
|
||||
}
|
||||
astWriter.hoist(ast);
|
||||
astWriter.print(ast);
|
||||
writer.softNewLine();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2015 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.jso.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.javascript.spi.GeneratorContext;
|
||||
import org.teavm.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class JSBodyBloatedEmitter implements JSBodyEmitter {
|
||||
private boolean isStatic;
|
||||
private MethodReference method;
|
||||
private String script;
|
||||
private String[] parameterNames;
|
||||
|
||||
public JSBodyBloatedEmitter(boolean isStatic, MethodReference method, String script, String[] parameterNames) {
|
||||
this.isStatic = isStatic;
|
||||
this.method = method;
|
||||
this.script = script;
|
||||
this.parameterNames = parameterNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(InjectorContext context) throws IOException {
|
||||
emit(context.getWriter(), index -> context.writeExpr(context.getArgument(index)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
emit(writer, index -> writer.append(context.getParameterName(index + 1)));
|
||||
}
|
||||
|
||||
private void emit(SourceWriter writer, EmissionStrategy strategy) throws IOException {
|
||||
int bodyParamCount = isStatic ? method.parameterCount() : method.parameterCount() - 1;
|
||||
|
||||
writer.append("if (!").appendMethodBody(method).append(".$native)").ws().append('{').indent().newLine();
|
||||
writer.appendMethodBody(method).append(".$native").ws().append('=').ws().append("function(");
|
||||
int count = method.parameterCount();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
writer.append('_').append(i);
|
||||
}
|
||||
writer.append(')').ws().append('{').softNewLine().indent();
|
||||
|
||||
writer.append("return (function(");
|
||||
for (int i = 0; i < bodyParamCount; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
String name = parameterNames[i];
|
||||
writer.append(name);
|
||||
}
|
||||
writer.append(')').ws().append('{').softNewLine().indent();
|
||||
writer.append(script).softNewLine();
|
||||
writer.outdent().append("})");
|
||||
if (!isStatic) {
|
||||
writer.append(".call");
|
||||
}
|
||||
writer.append('(');
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
writer.append('_').append(i);
|
||||
}
|
||||
writer.append(");").softNewLine();
|
||||
writer.outdent().append("};").softNewLine();
|
||||
writer.appendMethodBody(method).ws().append('=').ws().appendMethodBody(method).append(".$native;")
|
||||
.softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
|
||||
writer.append("return ").appendMethodBody(method).append('(');
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
strategy.emitArgument(i);
|
||||
}
|
||||
writer.append(");").softNewLine();
|
||||
}
|
||||
|
||||
interface EmissionStrategy {
|
||||
void emitArgument(int argument) throws IOException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2015 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.jso.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.javascript.spi.GeneratorContext;
|
||||
import org.teavm.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
interface JSBodyEmitter {
|
||||
void emit(InjectorContext context) throws IOException;
|
||||
|
||||
void emit(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException;
|
||||
}
|
|
@ -16,113 +16,29 @@
|
|||
package org.teavm.jso.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import org.mozilla.javascript.CompilerEnvirons;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.javascript.spi.Generator;
|
||||
import org.teavm.javascript.spi.GeneratorContext;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.javascript.spi.Injector;
|
||||
import org.teavm.javascript.spi.InjectorContext;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class JSBodyGenerator implements Generator {
|
||||
public class JSBodyGenerator implements Injector, Generator {
|
||||
@Override
|
||||
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||
JSBodyRepository emitterRepository = context.getService(JSBodyRepository.class);
|
||||
JSBodyEmitter emitter = emitterRepository.emitters.get(methodRef);
|
||||
emitter.emit(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
|
||||
MethodReader method = cls.getMethod(methodRef.getDescriptor());
|
||||
AnnotationReader annot = method.getAnnotations().get(JSBodyImpl.class.getName());
|
||||
boolean isStatic = annot.getValue("isStatic").getBoolean();
|
||||
List<AnnotationValue> paramNames = annot.getValue("params").getList();
|
||||
String script = annot.getValue("script").getString();
|
||||
|
||||
TeaVMErrorReporter errorReporter = new TeaVMErrorReporter(context.getDiagnostics(),
|
||||
new CallLocation(methodRef));
|
||||
CompilerEnvirons env = new CompilerEnvirons();
|
||||
env.setRecoverFromErrors(true);
|
||||
env.setLanguageVersion(Context.VERSION_1_8);
|
||||
env.setIdeMode(true);
|
||||
JSParser parser = new JSParser(env, errorReporter);
|
||||
parser.enterFunction();
|
||||
AstRoot rootNode = parser.parse(new StringReader(script), null, 0);
|
||||
parser.exitFunction();
|
||||
if (errorReporter.hasErrors()) {
|
||||
generateBloated(context, writer, methodRef, method, isStatic, paramNames, script);
|
||||
return;
|
||||
}
|
||||
|
||||
AstWriter astWriter = new AstWriter(writer);
|
||||
int paramIndex = 1;
|
||||
if (!isStatic) {
|
||||
astWriter.declareAlias("this", context.getParameterName(paramIndex++));
|
||||
}
|
||||
for (int i = 0; i < paramNames.size(); ++i) {
|
||||
astWriter.declareAlias(paramNames.get(i).getString(), context.getParameterName(paramIndex++));
|
||||
}
|
||||
astWriter.hoist(rootNode);
|
||||
astWriter.print(rootNode);
|
||||
writer.softNewLine();
|
||||
}
|
||||
|
||||
private void generateBloated(GeneratorContext context, SourceWriter writer, MethodReference methodRef,
|
||||
MethodReader method, boolean isStatic, List<AnnotationValue> paramNames, String script)
|
||||
throws IOException {
|
||||
int bodyParamCount = isStatic ? method.parameterCount() : method.parameterCount() - 1;
|
||||
|
||||
writer.append("if (!").appendMethodBody(methodRef).append(".$native)").ws().append('{').indent().newLine();
|
||||
writer.appendMethodBody(methodRef).append(".$native").ws().append('=').ws().append("function(");
|
||||
int count = method.parameterCount();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
writer.append('_').append(context.getParameterName(i + 1));
|
||||
}
|
||||
writer.append(')').ws().append('{').softNewLine().indent();
|
||||
|
||||
writer.append("return (function(");
|
||||
for (int i = 0; i < bodyParamCount; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
String name = paramNames.get(i).getString();
|
||||
writer.append(name);
|
||||
}
|
||||
writer.append(')').ws().append('{').softNewLine().indent();
|
||||
writer.append(script).softNewLine();
|
||||
writer.outdent().append("})");
|
||||
if (!isStatic) {
|
||||
writer.append(".call");
|
||||
}
|
||||
writer.append('(');
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
writer.append('_').append(context.getParameterName(i + 1));
|
||||
}
|
||||
writer.append(");").softNewLine();
|
||||
writer.outdent().append("};").softNewLine();
|
||||
writer.appendMethodBody(methodRef).ws().append('=').ws().appendMethodBody(methodRef).append(".$native;")
|
||||
.softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
|
||||
writer.append("return ").appendMethodBody(methodRef).append('(');
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(',').ws();
|
||||
}
|
||||
writer.append(context.getParameterName(i + 1));
|
||||
}
|
||||
writer.append(");").softNewLine();
|
||||
JSBodyRepository emitterRepository = context.getService(JSBodyRepository.class);
|
||||
JSBodyEmitter emitter = emitterRepository.emitters.get(methodRef);
|
||||
emitter.emit(context, writer, methodRef);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,10 +26,6 @@ import java.lang.annotation.Target;
|
|||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@interface JSBodyImpl {
|
||||
String[] params();
|
||||
|
||||
String script();
|
||||
|
||||
boolean isStatic();
|
||||
@interface JSBodyRef {
|
||||
String method();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2015 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.jso.plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class JSBodyRepository {
|
||||
public final Map<MethodReference, JSBodyEmitter> emitters = new HashMap<>();
|
||||
public final Map<MethodReference, MethodReference> methodMap = new HashMap<>();
|
||||
public final Set<MethodReference> processedMethods = new HashSet<>();
|
||||
}
|
|
@ -25,7 +25,9 @@ import org.teavm.vm.spi.TeaVMPlugin;
|
|||
public class JSOPlugin implements TeaVMPlugin {
|
||||
@Override
|
||||
public void install(TeaVMHost host) {
|
||||
host.add(new JSObjectClassTransformer());
|
||||
JSBodyRepository repository = new JSBodyRepository();
|
||||
host.registerService(JSBodyRepository.class, repository);
|
||||
host.add(new JSObjectClassTransformer(repository));
|
||||
JSODependencyListener dependencyListener = new JSODependencyListener();
|
||||
JSOAliasRenderer aliasRenderer = new JSOAliasRenderer(dependencyListener);
|
||||
host.add(dependencyListener);
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
package org.teavm.jso.plugin;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -25,6 +28,11 @@ import org.teavm.model.*;
|
|||
*/
|
||||
public class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||
private JavascriptNativeProcessor processor;
|
||||
private JSBodyRepository repository;
|
||||
|
||||
public JSObjectClassTransformer(JSBodyRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
|
@ -46,12 +54,10 @@ public class JSObjectClassTransformer implements ClassHolderTransformer {
|
|||
}
|
||||
}
|
||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||
processor.processJSBody(cls, method);
|
||||
} else if (method.getProgram() != null
|
||||
&& method.getAnnotations().get(JSBodyImpl.class.getName()) == null) {
|
||||
processor.processProgram(method);
|
||||
if (method.getProgram() != null) {
|
||||
processor.processProgram(repository, method);
|
||||
}
|
||||
}
|
||||
processor.createJSMethods(repository, cls);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.jso.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
@ -23,6 +25,9 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import org.mozilla.javascript.CompilerEnvirons;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.javascript.spi.GeneratedBy;
|
||||
import org.teavm.javascript.spi.Sync;
|
||||
|
@ -60,7 +65,6 @@ import org.teavm.model.Variable;
|
|||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
|
@ -275,7 +279,7 @@ class JavascriptNativeProcessor {
|
|||
return staticSignature;
|
||||
}
|
||||
|
||||
public void processProgram(MethodHolder methodToProcess) {
|
||||
public void processProgram(JSBodyRepository repository, MethodHolder methodToProcess) {
|
||||
program = methodToProcess.getProgram();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
|
@ -286,146 +290,218 @@ class JavascriptNativeProcessor {
|
|||
continue;
|
||||
}
|
||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
||||
if (!nativeRepos.isJavaScriptClass(invoke.getMethod().getClassName())) {
|
||||
continue;
|
||||
}
|
||||
replacement.clear();
|
||||
|
||||
MethodReader method = getMethod(invoke.getMethod());
|
||||
if (method == null || method.hasModifier(ElementModifier.STATIC)) {
|
||||
if (method == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.hasModifier(ElementModifier.FINAL)) {
|
||||
MethodReader overriden = getOverridenMethod(method);
|
||||
if (overriden != null) {
|
||||
CallLocation callLocation = new CallLocation(methodToProcess.getReference(),
|
||||
insn.getLocation());
|
||||
diagnostics.error(callLocation, "JS final method {{m0}} overrides {{M1}}. "
|
||||
+ "Overriding final method of overlay types is prohibited.",
|
||||
method.getReference(), overriden.getReference());
|
||||
}
|
||||
if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) {
|
||||
invoke.setMethod(new MethodReference(method.getOwnerName(), method.getName() + "$static",
|
||||
getStaticSignature(method.getReference())));
|
||||
invoke.getArguments().add(0, invoke.getInstance());
|
||||
invoke.setInstance(null);
|
||||
}
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
CallLocation callLocation = new CallLocation(methodToProcess.getReference(), insn.getLocation());
|
||||
if (method.getAnnotations().get(JSProperty.class.getName()) != null) {
|
||||
if (isProperGetter(method.getDescriptor())) {
|
||||
String propertyName;
|
||||
AnnotationReader annot = method.getAnnotations().get(JSProperty.class.getName());
|
||||
if (annot.getValue("value") != null) {
|
||||
propertyName = annot.getValue("value").getString();
|
||||
} else {
|
||||
propertyName = method.getName().charAt(0) == 'i' ? cutPrefix(method.getName(), 2)
|
||||
: cutPrefix(method.getName(), 3);
|
||||
}
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
} else if (isProperSetter(method.getDescriptor())) {
|
||||
String propertyName;
|
||||
AnnotationReader annot = method.getAnnotations().get(JSProperty.class.getName());
|
||||
if (annot.getValue("value") != null) {
|
||||
propertyName = annot.getValue("value").getString();
|
||||
} else {
|
||||
propertyName = cutPrefix(method.getName(), 3);
|
||||
}
|
||||
Variable wrapped = wrapArgument(callLocation, invoke.getArguments().get(0),
|
||||
method.parameterType(0));
|
||||
addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation());
|
||||
} else {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript property "
|
||||
+ "declaration", invoke.getMethod());
|
||||
continue;
|
||||
}
|
||||
} else if (method.getAnnotations().get(JSIndexer.class.getName()) != null) {
|
||||
if (isProperGetIndexer(method.getDescriptor())) {
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
addIndexerGet(invoke.getInstance(), wrap(invoke.getArguments().get(0),
|
||||
method.parameterType(0), invoke.getLocation()), result, invoke.getLocation());
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
} else if (isProperSetIndexer(method.getDescriptor())) {
|
||||
Variable index = wrap(invoke.getArguments().get(0), method.parameterType(0),
|
||||
invoke.getLocation());
|
||||
Variable value = wrap(invoke.getArguments().get(1), method.parameterType(1),
|
||||
invoke.getLocation());
|
||||
addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation());
|
||||
} else {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript indexer "
|
||||
+ "declaration", invoke.getMethod());
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
String name = method.getName();
|
||||
|
||||
AnnotationReader methodAnnot = method.getAnnotations().get(JSMethod.class.getName());
|
||||
if (methodAnnot != null) {
|
||||
AnnotationValue redefinedMethodName = methodAnnot.getValue("value");
|
||||
if (redefinedMethodName != null) {
|
||||
name = redefinedMethodName.getString();
|
||||
}
|
||||
}
|
||||
if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method "
|
||||
+ "declaration", invoke.getMethod());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (ValueType arg : method.getParameterTypes()) {
|
||||
if (!isSupportedType(arg)) {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method "
|
||||
+ "or constructor declaration", invoke.getMethod());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
InvokeInstruction newInvoke = new InvokeInstruction();
|
||||
ValueType[] signature = new ValueType[method.parameterCount() + 3];
|
||||
Arrays.fill(signature, ValueType.object(JSObject.class.getName()));
|
||||
newInvoke.setMethod(new MethodReference(JS.class.getName(), "invoke", signature));
|
||||
newInvoke.setType(InvocationType.SPECIAL);
|
||||
newInvoke.setReceiver(result);
|
||||
newInvoke.getArguments().add(invoke.getInstance());
|
||||
newInvoke.getArguments().add(addStringWrap(addString(name, invoke.getLocation()),
|
||||
invoke.getLocation()));
|
||||
newInvoke.setLocation(invoke.getLocation());
|
||||
for (int k = 0; k < invoke.getArguments().size(); ++k) {
|
||||
Variable arg = wrapArgument(callLocation, invoke.getArguments().get(k),
|
||||
method.parameterType(k));
|
||||
newInvoke.getArguments().add(arg);
|
||||
}
|
||||
replacement.add(newInvoke);
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
replacement.clear();
|
||||
if (processInvocation(repository, method, callLocation, invoke)) {
|
||||
block.getInstructions().set(j, replacement.get(0));
|
||||
block.getInstructions().addAll(j + 1, replacement.subList(1, replacement.size()));
|
||||
j += replacement.size() - 1;
|
||||
}
|
||||
block.getInstructions().set(j, replacement.get(0));
|
||||
block.getInstructions().addAll(j + 1, replacement.subList(1, replacement.size()));
|
||||
j += replacement.size() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void processJSBody(ClassHolder cls, MethodHolder methodToProcess) {
|
||||
private boolean processInvocation(JSBodyRepository repository, MethodReader method, CallLocation callLocation,
|
||||
InvokeInstruction invoke) {
|
||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||
return processJSBodyInvocation(repository, method, callLocation, invoke);
|
||||
}
|
||||
|
||||
if (!nativeRepos.isJavaScriptClass(invoke.getMethod().getClassName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method == null || method.hasModifier(ElementModifier.STATIC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method.hasModifier(ElementModifier.FINAL)) {
|
||||
MethodReader overriden = getOverridenMethod(method);
|
||||
if (overriden != null) {
|
||||
diagnostics.error(callLocation, "JS final method {{m0}} overrides {{M1}}. "
|
||||
+ "Overriding final method of overlay types is prohibited.",
|
||||
method.getReference(), overriden.getReference());
|
||||
}
|
||||
if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) {
|
||||
invoke.setMethod(new MethodReference(method.getOwnerName(), method.getName() + "$static",
|
||||
getStaticSignature(method.getReference())));
|
||||
invoke.getArguments().add(0, invoke.getInstance());
|
||||
invoke.setInstance(null);
|
||||
}
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method.getAnnotations().get(JSProperty.class.getName()) != null) {
|
||||
return processProperty(method, callLocation, invoke);
|
||||
} else if (method.getAnnotations().get(JSIndexer.class.getName()) != null) {
|
||||
return processIndexer(method, callLocation, invoke);
|
||||
} else {
|
||||
return processMethod(method, callLocation, invoke);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processJSBodyInvocation(JSBodyRepository repository, MethodReader method,
|
||||
CallLocation callLocation, InvokeInstruction invoke) {
|
||||
requireJSBody(repository, diagnostics, method);
|
||||
MethodReference delegate = repository.methodMap.get(method.getReference());
|
||||
if (delegate == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
InvokeInstruction newInvoke = new InvokeInstruction();
|
||||
ValueType[] signature = new ValueType[method.parameterCount() + 3];
|
||||
Arrays.fill(signature, ValueType.object(JSObject.class.getName()));
|
||||
newInvoke.setMethod(delegate);
|
||||
newInvoke.setType(InvocationType.SPECIAL);
|
||||
newInvoke.setReceiver(result);
|
||||
newInvoke.setLocation(invoke.getLocation());
|
||||
if (invoke.getInstance() != null) {
|
||||
Variable arg = wrapArgument(callLocation, invoke.getInstance(), ValueType.object(method.getOwnerName()));
|
||||
newInvoke.getArguments().add(arg);
|
||||
}
|
||||
for (int k = 0; k < invoke.getArguments().size(); ++k) {
|
||||
Variable arg = wrapArgument(callLocation, invoke.getArguments().get(k),
|
||||
method.parameterType(k));
|
||||
newInvoke.getArguments().add(arg);
|
||||
}
|
||||
replacement.add(newInvoke);
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean processProperty(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
|
||||
if (isProperGetter(method.getDescriptor())) {
|
||||
String propertyName;
|
||||
AnnotationReader annot = method.getAnnotations().get(JSProperty.class.getName());
|
||||
if (annot.getValue("value") != null) {
|
||||
propertyName = annot.getValue("value").getString();
|
||||
} else {
|
||||
propertyName = method.getName().charAt(0) == 'i' ? cutPrefix(method.getName(), 2)
|
||||
: cutPrefix(method.getName(), 3);
|
||||
}
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (isProperSetter(method.getDescriptor())) {
|
||||
String propertyName;
|
||||
AnnotationReader annot = method.getAnnotations().get(JSProperty.class.getName());
|
||||
if (annot.getValue("value") != null) {
|
||||
propertyName = annot.getValue("value").getString();
|
||||
} else {
|
||||
propertyName = cutPrefix(method.getName(), 3);
|
||||
}
|
||||
Variable wrapped = wrapArgument(callLocation, invoke.getArguments().get(0),
|
||||
method.parameterType(0));
|
||||
addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation());
|
||||
return true;
|
||||
}
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript property "
|
||||
+ "declaration", invoke.getMethod());
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processIndexer(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
|
||||
if (isProperGetIndexer(method.getDescriptor())) {
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
addIndexerGet(invoke.getInstance(), wrap(invoke.getArguments().get(0),
|
||||
method.parameterType(0), invoke.getLocation()), result, invoke.getLocation());
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (isProperSetIndexer(method.getDescriptor())) {
|
||||
Variable index = wrap(invoke.getArguments().get(0), method.parameterType(0),
|
||||
invoke.getLocation());
|
||||
Variable value = wrap(invoke.getArguments().get(1), method.parameterType(1),
|
||||
invoke.getLocation());
|
||||
addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation());
|
||||
return true;
|
||||
}
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript indexer "
|
||||
+ "declaration", invoke.getMethod());
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processMethod(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) {
|
||||
String name = method.getName();
|
||||
|
||||
AnnotationReader methodAnnot = method.getAnnotations().get(JSMethod.class.getName());
|
||||
if (methodAnnot != null) {
|
||||
AnnotationValue redefinedMethodName = methodAnnot.getValue("value");
|
||||
if (redefinedMethodName != null) {
|
||||
name = redefinedMethodName.getString();
|
||||
}
|
||||
}
|
||||
if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method "
|
||||
+ "declaration", invoke.getMethod());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ValueType arg : method.getParameterTypes()) {
|
||||
if (!isSupportedType(arg)) {
|
||||
diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method "
|
||||
+ "or constructor declaration", invoke.getMethod());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
|
||||
InvokeInstruction newInvoke = new InvokeInstruction();
|
||||
ValueType[] signature = new ValueType[method.parameterCount() + 3];
|
||||
Arrays.fill(signature, ValueType.object(JSObject.class.getName()));
|
||||
newInvoke.setMethod(new MethodReference(JS.class.getName(), "invoke", signature));
|
||||
newInvoke.setType(InvocationType.SPECIAL);
|
||||
newInvoke.setReceiver(result);
|
||||
newInvoke.getArguments().add(invoke.getInstance());
|
||||
newInvoke.getArguments().add(addStringWrap(addString(name, invoke.getLocation()),
|
||||
invoke.getLocation()));
|
||||
newInvoke.setLocation(invoke.getLocation());
|
||||
for (int k = 0; k < invoke.getArguments().size(); ++k) {
|
||||
Variable arg = wrapArgument(callLocation, invoke.getArguments().get(k),
|
||||
method.parameterType(k));
|
||||
newInvoke.getArguments().add(arg);
|
||||
}
|
||||
replacement.add(newInvoke);
|
||||
if (result != null) {
|
||||
result = unwrap(callLocation, result, method.getResultType());
|
||||
copyVar(result, invoke.getReceiver(), invoke.getLocation());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void requireJSBody(JSBodyRepository repository, Diagnostics diagnostics, MethodReader methodToProcess) {
|
||||
if (!repository.processedMethods.add(methodToProcess.getReference())) {
|
||||
return;
|
||||
}
|
||||
processJSBody(repository, diagnostics, methodToProcess);
|
||||
}
|
||||
|
||||
private void processJSBody(JSBodyRepository repository, Diagnostics diagnostics, MethodReader methodToProcess) {
|
||||
CallLocation location = new CallLocation(methodToProcess.getReference());
|
||||
boolean isStatic = methodToProcess.hasModifier(ElementModifier.STATIC);
|
||||
|
||||
// validate parameter names
|
||||
AnnotationHolder bodyAnnot = methodToProcess.getAnnotations().get(JSBody.class.getName());
|
||||
AnnotationReader bodyAnnot = methodToProcess.getAnnotations().get(JSBody.class.getName());
|
||||
int jsParamCount = bodyAnnot.getValue("params").getList().size();
|
||||
if (methodToProcess.parameterCount() != jsParamCount) {
|
||||
diagnostics.error(location, "JSBody method {{m0}} declares " + methodToProcess.parameterCount()
|
||||
|
@ -433,10 +509,6 @@ class JavascriptNativeProcessor {
|
|||
return;
|
||||
}
|
||||
|
||||
// remove annotation and make non-native
|
||||
methodToProcess.getAnnotations().remove(JSBody.class.getName());
|
||||
methodToProcess.getModifiers().remove(ElementModifier.NATIVE);
|
||||
|
||||
// generate parameter types for original method and validate
|
||||
int paramCount = methodToProcess.parameterCount();
|
||||
if (!isStatic) {
|
||||
|
@ -445,11 +517,11 @@ class JavascriptNativeProcessor {
|
|||
ValueType[] paramTypes = new ValueType[paramCount];
|
||||
int offset = 0;
|
||||
if (!isStatic) {
|
||||
ValueType paramType = ValueType.object(cls.getName());
|
||||
ValueType paramType = ValueType.object(methodToProcess.getOwnerName());
|
||||
paramTypes[offset++] = paramType;
|
||||
if (!isSupportedType(paramType)) {
|
||||
diagnostics.error(location, "Non-static JSBody method {{m0}} is owned by non-JS class {{c1}}",
|
||||
methodToProcess.getReference(), cls.getName());
|
||||
methodToProcess.getReference(), methodToProcess.getOwnerName());
|
||||
}
|
||||
}
|
||||
if (methodToProcess.getResultType() != ValueType.VOID && !isSupportedType(methodToProcess.getResultType())) {
|
||||
|
@ -470,52 +542,58 @@ class JavascriptNativeProcessor {
|
|||
: ValueType.parse(JSObject.class);
|
||||
|
||||
// create proxy method
|
||||
MethodHolder proxyMethod = new MethodHolder("$js_body$_" + methodIndexGenerator++, proxyParamTypes);
|
||||
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
||||
AnnotationHolder genBodyAnnot = new AnnotationHolder(JSBodyImpl.class.getName());
|
||||
genBodyAnnot.getValues().put("script", bodyAnnot.getValue("script"));
|
||||
genBodyAnnot.getValues().put("params", bodyAnnot.getValue("params"));
|
||||
genBodyAnnot.getValues().put("isStatic", new AnnotationValue(isStatic));
|
||||
AnnotationHolder generatorAnnot = new AnnotationHolder(GeneratedBy.class.getName());
|
||||
generatorAnnot.getValues().put("value", new AnnotationValue(ValueType.parse(JSBodyGenerator.class)));
|
||||
proxyMethod.getAnnotations().add(genBodyAnnot);
|
||||
proxyMethod.getAnnotations().add(generatorAnnot);
|
||||
cls.addMethod(proxyMethod);
|
||||
MethodReference proxyMethod = new MethodReference(methodToProcess.getOwnerName(),
|
||||
methodToProcess.getName() + "$js_body$_" + methodIndexGenerator++, proxyParamTypes);
|
||||
String script = bodyAnnot.getValue("script").getString();
|
||||
String[] parameterNames = bodyAnnot.getValue("params").getList().stream()
|
||||
.map(ann -> ann.getString())
|
||||
.toArray(sz -> new String[sz]);
|
||||
|
||||
// create program that invokes proxy method
|
||||
program = new Program();
|
||||
BasicBlock block = program.createBasicBlock();
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
program.createVariable();
|
||||
// Parse JS script
|
||||
TeaVMErrorReporter errorReporter = new TeaVMErrorReporter(diagnostics,
|
||||
new CallLocation(methodToProcess.getReference()));
|
||||
CompilerEnvirons env = new CompilerEnvirons();
|
||||
env.setRecoverFromErrors(true);
|
||||
env.setLanguageVersion(Context.VERSION_1_8);
|
||||
env.setIdeMode(true);
|
||||
JSParser parser = new JSParser(env, errorReporter);
|
||||
parser.enterFunction();
|
||||
AstRoot rootNode;
|
||||
try {
|
||||
rootNode = parser.parse(new StringReader(script), null, 0);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("IO Error occured", e);
|
||||
}
|
||||
if (isStatic) {
|
||||
program.createVariable();
|
||||
}
|
||||
methodToProcess.setProgram(program);
|
||||
parser.exitFunction();
|
||||
|
||||
// Generate invoke instruction
|
||||
replacement.clear();
|
||||
InvokeInstruction invoke = new InvokeInstruction();
|
||||
invoke.setType(InvocationType.SPECIAL);
|
||||
invoke.setMethod(proxyMethod.getReference());
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
Variable var = program.variableAt(isStatic ? i + 1 : i);
|
||||
invoke.getArguments().add(wrapArgument(location, var, paramTypes[i]));
|
||||
if (errorReporter.hasErrors()) {
|
||||
repository.emitters.put(proxyMethod, new JSBodyBloatedEmitter(isStatic, proxyMethod,
|
||||
script, parameterNames));
|
||||
} else {
|
||||
repository.emitters.put(proxyMethod, new JSBodyAstEmitter(isStatic, rootNode,
|
||||
parameterNames));
|
||||
}
|
||||
block.getInstructions().addAll(replacement);
|
||||
block.getInstructions().add(invoke);
|
||||
repository.methodMap.put(methodToProcess.getReference(), proxyMethod);
|
||||
}
|
||||
|
||||
// Generate return
|
||||
ExitInstruction exit = new ExitInstruction();
|
||||
if (methodToProcess.getResultType() != ValueType.VOID) {
|
||||
replacement.clear();
|
||||
Variable result = program.createVariable();
|
||||
invoke.setReceiver(result);
|
||||
exit.setValueToReturn(unwrap(location, result, methodToProcess.getResultType()));
|
||||
block.getInstructions().addAll(replacement);
|
||||
public void createJSMethods(JSBodyRepository repository, ClassHolder cls) {
|
||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||
MethodReference methodRef = method.getReference();
|
||||
if (method.getAnnotations().get(JSBody.class.getName()) != null) {
|
||||
requireJSBody(repository, diagnostics, method);
|
||||
if (repository.methodMap.containsKey(method.getReference())) {
|
||||
MethodReference proxyRef = repository.methodMap.get(methodRef);
|
||||
MethodHolder proxyMethod = new MethodHolder(proxyRef.getDescriptor());
|
||||
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
||||
AnnotationHolder generatorAnnot = new AnnotationHolder(GeneratedBy.class.getName());
|
||||
generatorAnnot.getValues().put("value",
|
||||
new AnnotationValue(ValueType.parse(JSBodyGenerator.class)));
|
||||
proxyMethod.getAnnotations().add(generatorAnnot);
|
||||
cls.addMethod(proxyMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
block.getInstructions().add(exit);
|
||||
}
|
||||
|
||||
private void addPropertyGet(String propertyName, Variable instance, Variable receiver,
|
||||
|
@ -971,6 +1049,9 @@ class JavascriptNativeProcessor {
|
|||
|
||||
private MethodReader getMethod(MethodReference ref) {
|
||||
ClassReader cls = classSource.get(ref.getClassName());
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
MethodReader method = cls.getMethod(ref.getDescriptor());
|
||||
if (method != null) {
|
||||
return method;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2015 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.jso.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
interface NameEmitter {
|
||||
void emit() throws IOException;
|
||||
}
|
Loading…
Reference in New Issue
Block a user