mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
JS: introduce JS template engine to write runtime, rewrite several functions using this engine
This commit is contained in:
parent
350cff776e
commit
16cd0aaab2
|
@ -91,7 +91,7 @@ public class AstWriter {
|
|||
public static final int PRECEDENCE_COND = 16;
|
||||
public static final int PRECEDENCE_ASSIGN = 17;
|
||||
public static final int PRECEDENCE_COMMA = 18;
|
||||
private SourceWriter writer;
|
||||
protected final SourceWriter writer;
|
||||
private Map<String, NameEmitter> nameMap = new HashMap<>();
|
||||
private boolean rootScope = true;
|
||||
private Set<String> aliases = new HashSet<>();
|
||||
|
@ -495,7 +495,7 @@ public class AstWriter {
|
|||
writer.append(';');
|
||||
}
|
||||
|
||||
private void print(ElementGet node) throws IOException {
|
||||
protected void print(ElementGet node) throws IOException {
|
||||
print(node.getTarget(), PRECEDENCE_MEMBER);
|
||||
writer.append('[');
|
||||
print(node.getElement());
|
||||
|
@ -512,6 +512,10 @@ public class AstWriter {
|
|||
}
|
||||
|
||||
private void print(FunctionCall node, int precedence) throws IOException {
|
||||
if (intrinsic(node, precedence)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tryJavaInvocation(node)) {
|
||||
return;
|
||||
}
|
||||
|
@ -539,6 +543,10 @@ public class AstWriter {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean intrinsic(FunctionCall node, int precedence) throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean tryJavaInvocation(FunctionCall node) throws IOException {
|
||||
if (!(node.getTarget() instanceof PropertyGet)) {
|
||||
return false;
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 konsoletyper.
|
||||
*
|
||||
* 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.backend.javascript.rendering;
|
||||
|
||||
import org.mozilla.javascript.Token;
|
||||
import org.mozilla.javascript.ast.AstNode;
|
||||
import org.mozilla.javascript.ast.ExpressionStatement;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.InfixExpression;
|
||||
import org.mozilla.javascript.ast.KeywordLiteral;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.PropertyGet;
|
||||
import org.teavm.backend.javascript.codegen.NamingStrategy;
|
||||
import org.teavm.model.FieldReference;
|
||||
|
||||
public class RuntimeAstTransformer extends AstVisitor {
|
||||
private static final FieldReference MONITOR_FIELD = new FieldReference(
|
||||
"java.lang.Object", "monitor");
|
||||
private NamingStrategy names;
|
||||
|
||||
public RuntimeAstTransformer(NamingStrategy names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitExpressionStatement(ExpressionStatement node) {
|
||||
AstNode expression = node.getExpression();
|
||||
if (expression.getType() == Token.CALL) {
|
||||
FunctionCall call = (FunctionCall) expression;
|
||||
if (call.getTarget().getType() == Token.NAME) {
|
||||
String id = ((Name) call.getTarget()).getIdentifier();
|
||||
if (id.equals("$rt_initMonitorField")) {
|
||||
AstNode arg = call.getArguments().get(0);
|
||||
accept(arg);
|
||||
String fieldId = names.getNameFor(MONITOR_FIELD);
|
||||
PropertyGet propertyGet = new PropertyGet(arg, new Name(0, fieldId));
|
||||
KeywordLiteral nullExpr = new KeywordLiteral(0, 0, Token.NULL);
|
||||
InfixExpression assign = new InfixExpression(Token.ASSIGN, propertyGet, nullExpr, 0);
|
||||
node.setExpression(assign);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitExpressionStatement(node);
|
||||
}
|
||||
}
|
|
@ -24,14 +24,13 @@ import org.mozilla.javascript.CompilerEnvirons;
|
|||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.templating.TemplatingAstWriter;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.vm.RenderingException;
|
||||
|
||||
public class RuntimeRenderer {
|
||||
|
@ -39,8 +38,6 @@ public class RuntimeRenderer {
|
|||
private static final String THREAD_CLASS = Thread.class.getName();
|
||||
private static final String STE_CLASS = StackTraceElement.class.getName();
|
||||
|
||||
private static final MethodReference NPE_INIT_METHOD = new MethodReference(NullPointerException.class,
|
||||
"<init>", void.class);
|
||||
private static final MethodDescriptor STRING_INTERN_METHOD = new MethodDescriptor("intern", String.class);
|
||||
private static final MethodDescriptor CURRENT_THREAD_METHOD = new MethodDescriptor("currentThread",
|
||||
Thread.class);
|
||||
|
@ -64,18 +61,9 @@ public class RuntimeRenderer {
|
|||
public void renderRuntime() throws RenderingException {
|
||||
try {
|
||||
renderHandWrittenRuntime("runtime.js");
|
||||
renderSetCloneMethod();
|
||||
renderRuntimeCls();
|
||||
renderRuntimeString();
|
||||
renderRuntimeUnwrapString();
|
||||
renderRuntimeObjcls();
|
||||
renderRuntimeThrowablecls();
|
||||
renderRuntimeThrowableMethods();
|
||||
renderRuntimeNullCheck();
|
||||
renderRuntimeIntern();
|
||||
renderStringClassInit();
|
||||
renderRuntimeThreads();
|
||||
renderRuntimeCreateException();
|
||||
renderCreateStackTraceElement();
|
||||
renderSetStackTrace();
|
||||
renderThrowAIOOBE();
|
||||
|
@ -88,8 +76,7 @@ public class RuntimeRenderer {
|
|||
public void renderHandWrittenRuntime(String name) throws IOException {
|
||||
AstRoot ast = parseRuntime(name);
|
||||
ast.visit(new StringConstantElimination());
|
||||
new RuntimeAstTransformer(writer.getNaming()).accept(ast);
|
||||
var astWriter = new AstWriter(writer, new DefaultGlobalNameWriter(writer));
|
||||
var astWriter = new TemplatingAstWriter(writer);
|
||||
astWriter.hoist(ast);
|
||||
astWriter.print(ast);
|
||||
}
|
||||
|
@ -107,50 +94,6 @@ public class RuntimeRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private void renderSetCloneMethod() throws IOException {
|
||||
writer.append("function $rt_setCloneMethod(target, f)").ws().append("{").softNewLine().indent();
|
||||
writer.append("target.").appendMethod("clone", Object.class).ws().append('=').ws().append("f;").
|
||||
softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeCls() throws IOException {
|
||||
writer.append("function $rt_cls(cls)").ws().append("{").softNewLine().indent();
|
||||
writer.append("return ").appendMethodBody("java.lang.Class", "getClass",
|
||||
ValueType.object("org.teavm.platform.PlatformClass"),
|
||||
ValueType.object("java.lang.Class")).append("(cls);")
|
||||
.softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeString() throws IOException {
|
||||
MethodReference stringCons = new MethodReference(String.class, "<init>", Object.class, void.class);
|
||||
writer.append("function $rt_str(str)").ws().append("{").indent().softNewLine();
|
||||
writer.append("if (str === null) {").indent().softNewLine();
|
||||
writer.append("return null;").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
writer.append("return ").appendInit(stringCons).append("(str);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeUnwrapString() throws IOException {
|
||||
FieldReference stringChars = new FieldReference(STRING_CLASS, "nativeString");
|
||||
writer.append("function $rt_ustr(str)").ws().append("{").indent().softNewLine();
|
||||
writer.append("return str").ws().append("!==").ws().append("null");
|
||||
writer.ws().append("?").ws().append("str.").appendField(stringChars);
|
||||
writer.ws().append(":").ws().append("null").append(";").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeNullCheck() throws IOException {
|
||||
writer.append("function $rt_nullCheck(val) {").indent().softNewLine();
|
||||
writer.append("if (val === null) {").indent().softNewLine();
|
||||
writer.append("$rt_throw(").appendInit(NPE_INIT_METHOD).append("());").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
writer.append("return val;").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeIntern() throws IOException {
|
||||
if (!needInternMethod()) {
|
||||
writer.append("function $rt_intern(str) {").indent().softNewLine();
|
||||
|
@ -161,12 +104,6 @@ public class RuntimeRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private void renderStringClassInit() throws IOException {
|
||||
writer.append("function $rt_stringClassInit(str)").ws().append("{").indent().softNewLine();
|
||||
writer.appendClassInit("java.lang.String").append("();").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
}
|
||||
|
||||
private boolean needInternMethod() {
|
||||
ClassReader cls = classSource.get(STRING_CLASS);
|
||||
if (cls == null) {
|
||||
|
@ -176,10 +113,6 @@ public class RuntimeRenderer {
|
|||
return method != null && method.hasModifier(ElementModifier.NATIVE);
|
||||
}
|
||||
|
||||
private void renderRuntimeObjcls() throws IOException {
|
||||
writer.append("function $rt_objcls() { return ").appendClass("java.lang.Object").append("; }").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeThrowablecls() throws IOException {
|
||||
writer.append("function $rt_stecls()").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
|
@ -191,18 +124,6 @@ public class RuntimeRenderer {
|
|||
writer.append(";").softNewLine().outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeThrowableMethods() throws IOException {
|
||||
writer.append("function $rt_throwableMessage(t)").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
writer.appendMethodBody(Throwable.class, "getMessage", String.class).append("(t);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
|
||||
writer.append("function $rt_throwableCause(t)").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
writer.appendMethodBody(Throwable.class, "getCause", Throwable.class).append("(t);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeThreads() throws IOException {
|
||||
ClassReader threadCls = classSource.get(THREAD_CLASS);
|
||||
MethodReader currentThreadMethod = threadCls != null ? threadCls.getMethod(CURRENT_THREAD_METHOD) : null;
|
||||
|
@ -225,14 +146,6 @@ public class RuntimeRenderer {
|
|||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderRuntimeCreateException() throws IOException {
|
||||
writer.append("function $rt_createException(message)").ws().append("{").indent().softNewLine();
|
||||
writer.append("return ");
|
||||
writer.appendInit(new MethodReference(RuntimeException.class, "<init>", String.class, void.class));
|
||||
writer.append("(message);").softNewLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderCreateStackTraceElement() throws IOException {
|
||||
ClassReader cls = classSource.get(STACK_TRACE_ELEM_INIT.getClassName());
|
||||
MethodReader stackTraceElemInit = cls != null ? cls.getMethod(STACK_TRACE_ELEM_INIT.getDescriptor()) : null;
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright 2023 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.backend.javascript.templating;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.mozilla.javascript.ast.ElementGet;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
import org.mozilla.javascript.ast.Name;
|
||||
import org.mozilla.javascript.ast.StringLiteral;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.rendering.AstWriter;
|
||||
import org.teavm.backend.javascript.rendering.DefaultGlobalNameWriter;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class TemplatingAstWriter extends AstWriter {
|
||||
public TemplatingAstWriter(SourceWriter writer) {
|
||||
super(writer, new DefaultGlobalNameWriter(writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean intrinsic(FunctionCall node, int precedence) throws IOException {
|
||||
if (node.getTarget() instanceof Name) {
|
||||
var name = (Name) node.getTarget();
|
||||
if (name.getDefiningScope() == null) {
|
||||
return tryIntrinsicName(node, name.getIdentifier());
|
||||
}
|
||||
}
|
||||
return super.intrinsic(node, precedence);
|
||||
}
|
||||
|
||||
private boolean tryIntrinsicName(FunctionCall node, String name) throws IOException {
|
||||
switch (name) {
|
||||
case "teavm_javaClass":
|
||||
return writeJavaClass(node);
|
||||
case "teavm_javaMethod":
|
||||
return writeJavaMethod(node);
|
||||
case "teavm_javaConstructor":
|
||||
return writeJavaConstructor(node);
|
||||
case "teavm_javaClassInit":
|
||||
return writeJavaClassInit(node);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeJavaClass(FunctionCall node) throws IOException {
|
||||
if (node.getArguments().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
if (!(classArg instanceof StringLiteral)) {
|
||||
return false;
|
||||
}
|
||||
writer.appendClass(((StringLiteral) classArg).getValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean writeJavaMethod(FunctionCall node) throws IOException {
|
||||
if (node.getArguments().size() != 2) {
|
||||
return false;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
var methodArg = node.getArguments().get(1);
|
||||
if (!(classArg instanceof StringLiteral) || !(methodArg instanceof StringLiteral)) {
|
||||
return false;
|
||||
}
|
||||
var method = new MethodReference(((StringLiteral) classArg).getValue(),
|
||||
MethodDescriptor.parse(((StringLiteral) methodArg).getValue()));
|
||||
writer.appendMethodBody(method);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean writeJavaConstructor(FunctionCall node) throws IOException {
|
||||
if (node.getArguments().size() != 2) {
|
||||
return false;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
var methodArg = node.getArguments().get(1);
|
||||
if (!(classArg instanceof StringLiteral) || !(methodArg instanceof StringLiteral)) {
|
||||
return false;
|
||||
}
|
||||
var method = new MethodReference(((StringLiteral) classArg).getValue(), "<init>",
|
||||
MethodDescriptor.parseSignature(((StringLiteral) methodArg).getValue()));
|
||||
writer.appendInit(method);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean writeJavaClassInit(FunctionCall node) throws IOException {
|
||||
if (node.getArguments().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
var classArg = node.getArguments().get(0);
|
||||
if (!(classArg instanceof StringLiteral)) {
|
||||
return false;
|
||||
}
|
||||
writer.appendClassInit(((StringLiteral) classArg).getValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void print(ElementGet node) throws IOException {
|
||||
if (node.getElement() instanceof FunctionCall) {
|
||||
var call = (FunctionCall) node.getElement();
|
||||
if (call.getTarget() instanceof Name) {
|
||||
var name = (Name) call.getTarget();
|
||||
if (name.getDefiningScope() == null) {
|
||||
switch (name.getIdentifier()) {
|
||||
case "teavm_javaVirtualMethod":
|
||||
if (writeJavaVirtualMethod(node, call)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "teavm_javaField":
|
||||
if (writeJavaField(node, call)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.print(node);
|
||||
}
|
||||
|
||||
private boolean writeJavaVirtualMethod(ElementGet get, FunctionCall call) throws IOException {
|
||||
var arg = call.getArguments().get(0);
|
||||
if (!(arg instanceof StringLiteral)) {
|
||||
return false;
|
||||
}
|
||||
var method = MethodDescriptor.parse(((StringLiteral) arg).getValue());
|
||||
print(get.getTarget());
|
||||
writer.append('.').appendMethod(method);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean writeJavaField(ElementGet get, FunctionCall call) throws IOException {
|
||||
if (call.getArguments().size() != 2) {
|
||||
return false;
|
||||
}
|
||||
var classArg = call.getArguments().get(0);
|
||||
var fieldArg = call.getArguments().get(1);
|
||||
if (!(classArg instanceof StringLiteral) || !(fieldArg instanceof StringLiteral)) {
|
||||
return false;
|
||||
}
|
||||
var className = ((StringLiteral) classArg).getValue();
|
||||
var fieldName = ((StringLiteral) fieldArg).getValue();
|
||||
print(get.getTarget());
|
||||
writer.append('.').appendField(new FieldReference(className, fieldName));
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -929,4 +929,42 @@ function $rt_substring(string, start, end) {
|
|||
var result = start.substring(start, end - 1) + start.substring(end - 1, end);
|
||||
$rt_substringSink = ($rt_substringSink + result.charCodeAt(result.length - 1)) | 0;
|
||||
}
|
||||
var $rt_substringSink = 0;
|
||||
var $rt_substringSink = 0;
|
||||
|
||||
function $rt_setCloneMethod(target, method) {
|
||||
target[teavm_javaVirtualMethod('clone()Ljava/lang/Object;')] = method;
|
||||
}
|
||||
function $rt_cls(cls) {
|
||||
return teavm_javaMethod("java.lang.Class",
|
||||
"getClass(Lorg/teavm/platform/PlatformClass;)Ljava/lang/Class;")(cls);
|
||||
}
|
||||
function $rt_str(str) {
|
||||
if (str === null) {
|
||||
return null;
|
||||
}
|
||||
return teavm_javaConstructor("java.lang.String", "(Ljava/lang/Object;)V")(str);
|
||||
}
|
||||
function $rt_ustr(str) {
|
||||
return str === null ? null : str[teavm_javaField("java.lang.String", "nativeString")];
|
||||
}
|
||||
function $rt_nullCheck(val) {
|
||||
if (val === null) {
|
||||
$rt_throw(teavm_javaConstructor("java.lang.NullPointerException", "()V")());
|
||||
}
|
||||
return val;
|
||||
}
|
||||
function $rt_stringClassInit() {
|
||||
teavm_javaClassInit("java.lang.String")();
|
||||
}
|
||||
function $rt_objcls() {
|
||||
return teavm_javaClass("java.lang.Object");
|
||||
}
|
||||
function $rt_createException(message) {
|
||||
return teavm_javaConstructor("java.lang.RuntimeException", "(Ljava/lang/String;)V")(message);
|
||||
}
|
||||
function $rt_throwableMessage(t) {
|
||||
return teavm_javaMethod("java.lang.Throwable", "getMessage()Ljava/lang/String;")(t);
|
||||
}
|
||||
function $rt_throwableCause(t) {
|
||||
return teavm_javaMethod("java.lang.Throwable", "getCause()Ljava/lang/Throwable;")(t);
|
||||
}
|
Loading…
Reference in New Issue
Block a user