mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Improve JS templating so that it could be used in native generators
This commit is contained in:
parent
b006cbb206
commit
eb0f4fb090
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.DependencyNode;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
|
||||||
|
public class SystemDependencyPlugin implements DependencyPlugin {
|
||||||
|
@Override
|
||||||
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
switch (method.getReference().getName()) {
|
||||||
|
case "doArrayCopy":
|
||||||
|
reachArrayCopy(method);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reachArrayCopy(MethodDependency method) {
|
||||||
|
DependencyNode src = method.getVariable(1);
|
||||||
|
DependencyNode dest = method.getVariable(3);
|
||||||
|
src.getArrayItem().connect(dest.getArrayItem());
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,70 +19,20 @@ import java.io.IOException;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.backend.javascript.templating.JavaScriptTemplate;
|
||||||
import org.teavm.dependency.DependencyNode;
|
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||||
import org.teavm.dependency.DependencyPlugin;
|
|
||||||
import org.teavm.dependency.MethodDependency;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class SystemNativeGenerator implements Generator, DependencyPlugin {
|
public class SystemNativeGenerator implements Generator {
|
||||||
|
private JavaScriptTemplate template;
|
||||||
|
|
||||||
|
public SystemNativeGenerator(JavaScriptTemplateFactory templateFactory) throws IOException {
|
||||||
|
template = templateFactory.createFromResource("org/teavm/classlib/java/lang/System.js");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
switch (methodRef.getName()) {
|
var fragment = template.builder(methodRef.getName()).withContext(context).build();
|
||||||
case "doArrayCopy":
|
fragment.write(writer, 0);
|
||||||
generateArrayCopy(context, writer);
|
|
||||||
break;
|
|
||||||
case "currentTimeMillis":
|
|
||||||
generateCurrentTimeMillis(writer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
|
||||||
switch (method.getReference().getName()) {
|
|
||||||
case "doArrayCopy":
|
|
||||||
reachArrayCopy(method);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateArrayCopy(GeneratorContext context, SourceWriter writer) throws IOException {
|
|
||||||
String src = context.getParameterName(1);
|
|
||||||
String srcPos = context.getParameterName(2);
|
|
||||||
String dest = context.getParameterName(3);
|
|
||||||
String destPos = context.getParameterName(4);
|
|
||||||
String length = context.getParameterName(5);
|
|
||||||
writer.append("if").ws().append("(").append(length).ws().append("===").ws().append("0)").ws().append("{")
|
|
||||||
.indent().softNewLine();
|
|
||||||
writer.append("return;").ws().softNewLine();
|
|
||||||
writer.outdent().append("}").ws().append("else ");
|
|
||||||
writer.append("if").ws().append("(typeof " + src + ".data.buffer").ws().append("!==").ws()
|
|
||||||
.append("'undefined')").ws().append("{").indent().softNewLine();
|
|
||||||
writer.append(dest + ".data.set(" + src + ".data.subarray(" + srcPos + ",").ws()
|
|
||||||
.append(srcPos).ws().append("+").ws().append(length).append("),").ws()
|
|
||||||
.append(destPos).append(");").softNewLine();
|
|
||||||
writer.outdent().append("}").ws().append("else ");
|
|
||||||
writer.append("if (" + src + " !== " + dest + " || " + destPos + " < " + srcPos + ") {").indent().newLine();
|
|
||||||
writer.append("for (var i = 0; i < " + length + "; i = (i + 1) | 0) {").indent().softNewLine();
|
|
||||||
writer.append(dest + ".data[" + destPos + "++] = " + src + ".data[" + srcPos + "++];").softNewLine();
|
|
||||||
writer.outdent().append("}").softNewLine();
|
|
||||||
writer.outdent().append("}").ws().append("else").ws().append("{").indent().softNewLine();
|
|
||||||
writer.append(srcPos + " = (" + srcPos + " + " + length + ") | 0;").softNewLine();
|
|
||||||
writer.append(destPos + " = (" + destPos + " + " + length + ") | 0;").softNewLine();
|
|
||||||
writer.append("for (var i = 0; i < " + length + "; i = (i + 1) | 0) {").indent().softNewLine();
|
|
||||||
writer.append(dest + ".data[--" + destPos + "] = " + src + ".data[--" + srcPos + "];").softNewLine();
|
|
||||||
writer.outdent().append("}").softNewLine();
|
|
||||||
writer.outdent().append("}").softNewLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateCurrentTimeMillis(SourceWriter writer) throws IOException {
|
|
||||||
writer.append("return Long_fromNumber(new Date().getTime());").softNewLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reachArrayCopy(MethodDependency method) {
|
|
||||||
DependencyNode src = method.getVariable(1);
|
|
||||||
DependencyNode dest = method.getVariable(3);
|
|
||||||
src.getArrayItem().connect(dest.getArrayItem());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class TRuntime {
|
||||||
* its best effort to recycle all discarded objects. The name gc stands for
|
* its best effort to recycle all discarded objects. The name gc stands for
|
||||||
* "garbage collector". The Java Virtual Machine performs this recycling
|
* "garbage collector". The Java Virtual Machine performs this recycling
|
||||||
* process automatically as needed even if the gc method is not invoked
|
* process automatically as needed even if the gc method is not invoked
|
||||||
* explicitly. The method System.gc() is the conventional and convenient
|
* explicitly. The method System.js.gc() is the conventional and convenient
|
||||||
* means of invoking this method.
|
* means of invoking this method.
|
||||||
*/
|
*/
|
||||||
public void gc() {
|
public void gc() {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.teavm.classlib.java.io.TInputStream;
|
||||||
import org.teavm.classlib.java.io.TOutputStream;
|
import org.teavm.classlib.java.io.TOutputStream;
|
||||||
import org.teavm.classlib.java.io.TPrintStream;
|
import org.teavm.classlib.java.io.TPrintStream;
|
||||||
import org.teavm.classlib.java.lang.reflect.TArray;
|
import org.teavm.classlib.java.lang.reflect.TArray;
|
||||||
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
|
@ -125,6 +126,7 @@ public final class TSystem extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(SystemNativeGenerator.class)
|
@GeneratedBy(SystemNativeGenerator.class)
|
||||||
|
@PluggableDependency(SystemDependencyPlugin.class)
|
||||||
@DelegateTo("doArrayCopyLowLevel")
|
@DelegateTo("doArrayCopyLowLevel")
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
function doArrayCopy(src, srcPos, dest, destPos, length) {
|
||||||
|
if (length !== 0) {
|
||||||
|
if (typeof src.data.buffer !== 'undefined') {
|
||||||
|
dest.data.set(src.data.subarray(srcPos, srcPos + length), destPos);
|
||||||
|
} else if (src !== dest || destPos < srcPos) {
|
||||||
|
for (let i = 0; i < length; i = (i + 1) | 0) {
|
||||||
|
dest.data[destPos++] = src.data[srcPos++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
srcPos = (srcPos + length) | 0;
|
||||||
|
destPos = (destPos + length) | 0;
|
||||||
|
for (let i = 0; i < length; i = (i + 1) | 0) {
|
||||||
|
dest.data[--destPos] = src.data[--srcPos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function currentTimeMillis() {
|
||||||
|
return Long_fromNumber(new (teavm_globals.Date)().getTime());
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import java.io.Writer;
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
@ -70,11 +71,13 @@ import org.teavm.backend.javascript.spi.ModuleImporter;
|
||||||
import org.teavm.backend.javascript.spi.ModuleImporterContext;
|
import org.teavm.backend.javascript.spi.ModuleImporterContext;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
||||||
|
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||||
import org.teavm.cache.AstCacheEntry;
|
import org.teavm.cache.AstCacheEntry;
|
||||||
import org.teavm.cache.AstDependencyExtractor;
|
import org.teavm.cache.AstDependencyExtractor;
|
||||||
import org.teavm.cache.CacheStatus;
|
import org.teavm.cache.CacheStatus;
|
||||||
import org.teavm.cache.EmptyMethodNodeCache;
|
import org.teavm.cache.EmptyMethodNodeCache;
|
||||||
import org.teavm.cache.MethodNodeCache;
|
import org.teavm.cache.MethodNodeCache;
|
||||||
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
||||||
import org.teavm.debugging.information.SourceLocation;
|
import org.teavm.debugging.information.SourceLocation;
|
||||||
|
@ -148,6 +151,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
||||||
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
||||||
|
private Map<String, Generator> generatorCache = new HashMap<>();
|
||||||
|
private JavaScriptTemplateFactory templateFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ClassHolderTransformer> getTransformers() {
|
public List<ClassHolderTransformer> getTransformers() {
|
||||||
|
@ -175,6 +180,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
var refQueueGenerator = new ReferenceQueueGenerator();
|
var refQueueGenerator = new ReferenceQueueGenerator();
|
||||||
methodGenerators.put(new MethodReference(ReferenceQueue.class, "<init>", void.class), refQueueGenerator);
|
methodGenerators.put(new MethodReference(ReferenceQueue.class, "<init>", void.class), refQueueGenerator);
|
||||||
methodGenerators.put(new MethodReference(ReferenceQueue.class, "poll", Reference.class), refQueueGenerator);
|
methodGenerators.put(new MethodReference(ReferenceQueue.class, "poll", Reference.class), refQueueGenerator);
|
||||||
|
|
||||||
|
templateFactory = new JavaScriptTemplateFactory(controller.getClassLoader(),
|
||||||
|
controller.getDependencyInfo().getClassSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -629,7 +637,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
classNodes.add(decompile(decompiler, cls));
|
classNodes.add(decompile(decompiler, cls, classes));
|
||||||
}
|
}
|
||||||
return classNodes;
|
return classNodes;
|
||||||
}
|
}
|
||||||
|
@ -660,7 +668,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
order.add(className);
|
order.add(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PreparedClass decompile(Decompiler decompiler, ClassHolder cls) {
|
private PreparedClass decompile(Decompiler decompiler, ClassHolder cls, ClassReaderSource classes) {
|
||||||
PreparedClass clsNode = new PreparedClass(cls);
|
PreparedClass clsNode = new PreparedClass(cls);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||||
|
@ -675,14 +683,14 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
|
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
|
||||||
? decompileNative(method)
|
? decompileNative(method, classes)
|
||||||
: decompile(decompiler, method);
|
: decompile(decompiler, method);
|
||||||
clsNode.getMethods().add(preparedMethod);
|
clsNode.getMethods().add(preparedMethod);
|
||||||
}
|
}
|
||||||
return clsNode;
|
return clsNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PreparedMethod decompileNative(MethodHolder method) {
|
private PreparedMethod decompileNative(MethodHolder method, ClassReaderSource classes) {
|
||||||
MethodReference reference = method.getReference();
|
MethodReference reference = method.getReference();
|
||||||
Generator generator = methodGenerators.get(reference);
|
Generator generator = methodGenerators.get(reference);
|
||||||
if (generator == null && !isBootstrap()) {
|
if (generator == null && !isBootstrap()) {
|
||||||
|
@ -693,18 +701,62 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
||||||
String generatorClassName = ((ValueType.Object) annotValue).getClassName();
|
String generatorClassName = ((ValueType.Object) annotValue).getClassName();
|
||||||
try {
|
generator = generatorCache.computeIfAbsent(generatorClassName,
|
||||||
Class<?> generatorClass = Class.forName(generatorClassName, true, controller.getClassLoader());
|
name -> createGenerator(name, method, classes));
|
||||||
generator = (Generator) generatorClass.newInstance();
|
|
||||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
|
||||||
throw new DecompilationException("Error instantiating generator " + generatorClassName
|
|
||||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PreparedMethod(method, null, generator, asyncMethods.contains(reference), null);
|
return new PreparedMethod(method, null, generator, asyncMethods.contains(reference), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Generator createGenerator(String name, MethodHolder method, ClassReaderSource classes) {
|
||||||
|
Class<?> generatorClass;
|
||||||
|
try {
|
||||||
|
generatorClass = Class.forName(name, true, controller.getClassLoader());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructors = generatorClass.getConstructors();
|
||||||
|
if (constructors.length != 1) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructor = constructors[0];
|
||||||
|
var parameterTypes = constructor.getParameterTypes();
|
||||||
|
var arguments = new Object[parameterTypes.length];
|
||||||
|
for (var i = 0; i < arguments.length; ++i) {
|
||||||
|
var parameterType = parameterTypes[i];
|
||||||
|
if (parameterType.equals(ClassReaderSource.class)) {
|
||||||
|
arguments[i] = classes;
|
||||||
|
} else if (parameterType.equals(Properties.class)) {
|
||||||
|
arguments[i] = controller.getProperties();
|
||||||
|
} else if (parameterType.equals(DependencyInfo.class)) {
|
||||||
|
arguments[i] = controller.getDependencyInfo();
|
||||||
|
} else if (parameterType.equals(ServiceRepository.class)) {
|
||||||
|
arguments[i] = controller.getServices();
|
||||||
|
} else if (parameterType.equals(JavaScriptTemplateFactory.class)) {
|
||||||
|
arguments[i] = templateFactory;
|
||||||
|
} else {
|
||||||
|
var service = controller.getServices().getService(parameterType);
|
||||||
|
if (service == null) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor() + ". "
|
||||||
|
+ "Its constructor requires " + parameterType + " as its parameter #" + (i + 1)
|
||||||
|
+ " which is not available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (Generator) constructor.newInstance(arguments);
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new DecompilationException("Error instantiating generator " + name
|
||||||
|
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private PreparedMethod decompile(Decompiler decompiler, MethodHolder method) {
|
private PreparedMethod decompile(Decompiler decompiler, MethodHolder method) {
|
||||||
MethodReference reference = method.getReference();
|
MethodReference reference = method.getReference();
|
||||||
if (asyncMethods.contains(reference)) {
|
if (asyncMethods.contains(reference)) {
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.ast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import org.mozilla.javascript.CompilerEnvirons;
|
||||||
|
import org.mozilla.javascript.Context;
|
||||||
|
import org.mozilla.javascript.ast.AstNode;
|
||||||
|
import org.teavm.backend.javascript.rendering.JSParser;
|
||||||
|
|
||||||
|
public final class AstUtil {
|
||||||
|
private AstUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AstNode parse(String string) {
|
||||||
|
var env = new CompilerEnvirons();
|
||||||
|
env.setRecoverFromErrors(true);
|
||||||
|
env.setLanguageVersion(Context.VERSION_1_8);
|
||||||
|
var factory = new JSParser(env);
|
||||||
|
|
||||||
|
return factory.parse(string, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AstNode parseFromResources(ClassLoader classLoader, String path) throws IOException {
|
||||||
|
try (var input = classLoader.getResourceAsStream(path)) {
|
||||||
|
return parse(new String(input.readAllBytes(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -502,7 +502,7 @@ public class AstWriter {
|
||||||
writer.append(']');
|
writer.append(']');
|
||||||
}
|
}
|
||||||
|
|
||||||
private void print(PropertyGet node) throws IOException {
|
public void print(PropertyGet node) throws IOException {
|
||||||
print(node.getLeft(), PRECEDENCE_MEMBER);
|
print(node.getLeft(), PRECEDENCE_MEMBER);
|
||||||
writer.append('.');
|
writer.append('.');
|
||||||
var oldRootScope = rootScope;
|
var oldRootScope = rootScope;
|
||||||
|
@ -651,8 +651,8 @@ public class AstWriter {
|
||||||
writer.append(node.getQuoteCharacter());
|
writer.append(node.getQuoteCharacter());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void print(Name node, int precedence) throws IOException {
|
public void print(Name node, int precedence) throws IOException {
|
||||||
if (rootScope) {
|
if (rootScope && node.getDefiningScope() == null) {
|
||||||
var alias = nameMap.get(node.getIdentifier());
|
var alias = nameMap.get(node.getIdentifier());
|
||||||
if (alias == null) {
|
if (alias == null) {
|
||||||
if (globalNameWriter != null) {
|
if (globalNameWriter != null) {
|
||||||
|
@ -667,6 +667,10 @@ public class AstWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final boolean isRootScope() {
|
||||||
|
return rootScope;
|
||||||
|
}
|
||||||
|
|
||||||
private void print(RegExpLiteral node) throws IOException {
|
private void print(RegExpLiteral node) throws IOException {
|
||||||
writer.append('/').append(node.getValue()).append('/').append(node.getFlags());
|
writer.append('/').append(node.getValue()).append('/').append(node.getFlags());
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class RuntimeRenderer {
|
||||||
AstRoot ast = parseRuntime(name);
|
AstRoot ast = parseRuntime(name);
|
||||||
ast.visit(new StringConstantElimination());
|
ast.visit(new StringConstantElimination());
|
||||||
new TemplatingAstTransformer(classSource).visit(ast);
|
new TemplatingAstTransformer(classSource).visit(ast);
|
||||||
var astWriter = new TemplatingAstWriter(writer);
|
var astWriter = new TemplatingAstWriter(writer, null, null);
|
||||||
astWriter.hoist(ast);
|
astWriter.hoist(ast);
|
||||||
astWriter.print(ast);
|
astWriter.print(ast);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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.util.HashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
|
import org.mozilla.javascript.ast.AstNode;
|
||||||
|
import org.mozilla.javascript.ast.FunctionNode;
|
||||||
|
import org.mozilla.javascript.ast.Name;
|
||||||
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
|
||||||
|
public class JavaScriptTemplate {
|
||||||
|
private TemplatingFunctionIndex functionIndex = new TemplatingFunctionIndex();
|
||||||
|
|
||||||
|
public JavaScriptTemplate(AstNode node, ClassReaderSource classSource) {
|
||||||
|
new TemplatingAstTransformer(classSource).visit(node);
|
||||||
|
functionIndex.visit(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FragmentBuilder builder(String functionName) {
|
||||||
|
var function = functionIndex.getFunction(functionName);
|
||||||
|
if (function == null) {
|
||||||
|
throw new IllegalArgumentException("Function " + functionName + " was not found in JS template");
|
||||||
|
}
|
||||||
|
return new FragmentBuilder(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FragmentBuilder {
|
||||||
|
private FunctionNode node;
|
||||||
|
private IntFunction<SourceFragment> parameters;
|
||||||
|
|
||||||
|
private FragmentBuilder(FunctionNode node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FragmentBuilder withParameters(IntFunction<SourceFragment> parameters) {
|
||||||
|
this.parameters = parameters;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FragmentBuilder withContext(GeneratorContext context) {
|
||||||
|
return withParameters(param -> (writer, precedence) -> writer.append(context.getParameterName(param + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceFragment build() {
|
||||||
|
var intParameters = parameters;
|
||||||
|
var paramNameToIndex = new HashMap<String, Integer>();
|
||||||
|
for (var i = 0; i < node.getParams().size(); ++i) {
|
||||||
|
var param = node.getParams().get(i);
|
||||||
|
if (param instanceof Name) {
|
||||||
|
paramNameToIndex.put(((Name) param).getIdentifier(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Function<String, SourceFragment> nameParameters = name -> {
|
||||||
|
var index = paramNameToIndex.get(name);
|
||||||
|
return index != null ? intParameters.apply(index) : null;
|
||||||
|
};
|
||||||
|
var thisFragment = parameters.apply(0);
|
||||||
|
var body = node.getBody();
|
||||||
|
return (writer, precedence) -> {
|
||||||
|
var astWriter = new TemplatingAstWriter(writer, nameParameters, node);
|
||||||
|
if (thisFragment != null) {
|
||||||
|
astWriter.declareNameEmitter("this", thisPrecedence -> thisFragment.write(writer, thisPrecedence));
|
||||||
|
}
|
||||||
|
for (var child = body.getFirstChild(); child != null; child = child.getNext()) {
|
||||||
|
astWriter.print((AstNode) child);
|
||||||
|
writer.softNewLine();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.teavm.backend.javascript.ast.AstUtil;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
|
||||||
|
public class JavaScriptTemplateFactory {
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
|
||||||
|
public JavaScriptTemplateFactory(ClassLoader classLoader, ClassReaderSource classSource) {
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.classSource = classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JavaScriptTemplate createFromResource(String path) throws IOException {
|
||||||
|
return new JavaScriptTemplate(AstUtil.parseFromResources(classLoader, path), classSource);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
|
||||||
|
public interface SourceFragment {
|
||||||
|
void write(SourceWriter writer, int precedence) throws IOException;
|
||||||
|
}
|
|
@ -16,9 +16,12 @@
|
||||||
package org.teavm.backend.javascript.templating;
|
package org.teavm.backend.javascript.templating;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.function.Function;
|
||||||
import org.mozilla.javascript.ast.ElementGet;
|
import org.mozilla.javascript.ast.ElementGet;
|
||||||
import org.mozilla.javascript.ast.FunctionCall;
|
import org.mozilla.javascript.ast.FunctionCall;
|
||||||
import org.mozilla.javascript.ast.Name;
|
import org.mozilla.javascript.ast.Name;
|
||||||
|
import org.mozilla.javascript.ast.PropertyGet;
|
||||||
|
import org.mozilla.javascript.ast.Scope;
|
||||||
import org.mozilla.javascript.ast.StringLiteral;
|
import org.mozilla.javascript.ast.StringLiteral;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.rendering.AstWriter;
|
import org.teavm.backend.javascript.rendering.AstWriter;
|
||||||
|
@ -28,8 +31,13 @@ import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class TemplatingAstWriter extends AstWriter {
|
public class TemplatingAstWriter extends AstWriter {
|
||||||
public TemplatingAstWriter(SourceWriter writer) {
|
private Function<String, SourceFragment> names;
|
||||||
|
private Scope scope;
|
||||||
|
|
||||||
|
public TemplatingAstWriter(SourceWriter writer, Function<String, SourceFragment> names, Scope scope) {
|
||||||
super(writer, new DefaultGlobalNameWriter(writer));
|
super(writer, new DefaultGlobalNameWriter(writer));
|
||||||
|
this.names = names;
|
||||||
|
this.scope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -137,6 +145,19 @@ public class TemplatingAstWriter extends AstWriter {
|
||||||
super.print(node);
|
super.print(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(PropertyGet node) throws IOException {
|
||||||
|
if (node.getTarget() instanceof Name) {
|
||||||
|
var name = (Name) node.getTarget();
|
||||||
|
if (name.getDefiningScope() == null && name.getIdentifier().equals("teavm_globals")) {
|
||||||
|
writer.append("$rt_globals").append(".");
|
||||||
|
print(node.getProperty());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.print(node);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean writeJavaVirtualMethod(ElementGet get, FunctionCall call) throws IOException {
|
private boolean writeJavaVirtualMethod(ElementGet get, FunctionCall call) throws IOException {
|
||||||
var arg = call.getArguments().get(0);
|
var arg = call.getArguments().get(0);
|
||||||
if (!(arg instanceof StringLiteral)) {
|
if (!(arg instanceof StringLiteral)) {
|
||||||
|
@ -163,4 +184,22 @@ public class TemplatingAstWriter extends AstWriter {
|
||||||
writer.append('.').appendField(new FieldReference(className, fieldName));
|
writer.append('.').appendField(new FieldReference(className, fieldName));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void print(Name node, int precedence) throws IOException {
|
||||||
|
if (isRootScope()) {
|
||||||
|
if (names != null && node.getDefiningScope() == scope) {
|
||||||
|
var fragment = names.apply(node.getIdentifier());
|
||||||
|
if (fragment != null) {
|
||||||
|
fragment.write(writer, precedence);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.getDefiningScope() == null && scope != null) {
|
||||||
|
writer.appendFunction(node.getIdentifier());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.print(node, precedence);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.mozilla.javascript.ast.FunctionNode;
|
||||||
|
import org.teavm.backend.javascript.ast.AstVisitor;
|
||||||
|
|
||||||
|
public class TemplatingFunctionIndex extends AstVisitor {
|
||||||
|
private Map<String, FunctionNode> functions = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(FunctionNode node) {
|
||||||
|
if (node.getName() != null) {
|
||||||
|
functions.put(node.getName(), node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionNode getFunction(String name) {
|
||||||
|
return functions.get(name);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user