mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -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.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.DependencyPlugin;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.backend.javascript.templating.JavaScriptTemplate;
|
||||
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||
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
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
switch (methodRef.getName()) {
|
||||
case "doArrayCopy":
|
||||
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());
|
||||
var fragment = template.builder(methodRef.getName()).withContext(context).build();
|
||||
fragment.write(writer, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public class TRuntime {
|
|||
* its best effort to recycle all discarded objects. The name gc stands for
|
||||
* "garbage collector". The Java Virtual Machine performs this recycling
|
||||
* 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.
|
||||
*/
|
||||
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.TPrintStream;
|
||||
import org.teavm.classlib.java.lang.reflect.TArray;
|
||||
import org.teavm.dependency.PluggableDependency;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.Import;
|
||||
|
@ -125,6 +126,7 @@ public final class TSystem extends TObject {
|
|||
}
|
||||
|
||||
@GeneratedBy(SystemNativeGenerator.class)
|
||||
@PluggableDependency(SystemDependencyPlugin.class)
|
||||
@DelegateTo("doArrayCopyLowLevel")
|
||||
@NoSideEffects
|
||||
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.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
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.VirtualMethodContributor;
|
||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
||||
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||
import org.teavm.cache.AstCacheEntry;
|
||||
import org.teavm.cache.AstDependencyExtractor;
|
||||
import org.teavm.cache.CacheStatus;
|
||||
import org.teavm.cache.EmptyMethodNodeCache;
|
||||
import org.teavm.cache.MethodNodeCache;
|
||||
import org.teavm.common.ServiceRepository;
|
||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||
import org.teavm.debugging.information.DummyDebugInformationEmitter;
|
||||
import org.teavm.debugging.information.SourceLocation;
|
||||
|
@ -148,6 +151,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
||||
private final Map<String, String> importedModules = new LinkedHashMap<>();
|
||||
private Map<String, Generator> generatorCache = new HashMap<>();
|
||||
private JavaScriptTemplateFactory templateFactory;
|
||||
|
||||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
|
@ -175,6 +180,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
var refQueueGenerator = new ReferenceQueueGenerator();
|
||||
methodGenerators.put(new MethodReference(ReferenceQueue.class, "<init>", void.class), refQueueGenerator);
|
||||
methodGenerators.put(new MethodReference(ReferenceQueue.class, "poll", Reference.class), refQueueGenerator);
|
||||
|
||||
templateFactory = new JavaScriptTemplateFactory(controller.getClassLoader(),
|
||||
controller.getDependencyInfo().getClassSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -629,7 +637,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
break;
|
||||
}
|
||||
}
|
||||
classNodes.add(decompile(decompiler, cls));
|
||||
classNodes.add(decompile(decompiler, cls, classes));
|
||||
}
|
||||
return classNodes;
|
||||
}
|
||||
|
@ -660,7 +668,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
order.add(className);
|
||||
}
|
||||
|
||||
private PreparedClass decompile(Decompiler decompiler, ClassHolder cls) {
|
||||
private PreparedClass decompile(Decompiler decompiler, ClassHolder cls, ClassReaderSource classes) {
|
||||
PreparedClass clsNode = new PreparedClass(cls);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||
|
@ -675,14 +683,14 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
}
|
||||
|
||||
PreparedMethod preparedMethod = method.hasModifier(ElementModifier.NATIVE)
|
||||
? decompileNative(method)
|
||||
? decompileNative(method, classes)
|
||||
: decompile(decompiler, method);
|
||||
clsNode.getMethods().add(preparedMethod);
|
||||
}
|
||||
return clsNode;
|
||||
}
|
||||
|
||||
private PreparedMethod decompileNative(MethodHolder method) {
|
||||
private PreparedMethod decompileNative(MethodHolder method, ClassReaderSource classes) {
|
||||
MethodReference reference = method.getReference();
|
||||
Generator generator = methodGenerators.get(reference);
|
||||
if (generator == null && !isBootstrap()) {
|
||||
|
@ -693,18 +701,62 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
}
|
||||
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
||||
String generatorClassName = ((ValueType.Object) annotValue).getClassName();
|
||||
try {
|
||||
Class<?> generatorClass = Class.forName(generatorClassName, true, controller.getClassLoader());
|
||||
generator = (Generator) generatorClass.newInstance();
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw new DecompilationException("Error instantiating generator " + generatorClassName
|
||||
+ " for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||
}
|
||||
generator = generatorCache.computeIfAbsent(generatorClassName,
|
||||
name -> createGenerator(name, method, classes));
|
||||
}
|
||||
|
||||
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) {
|
||||
MethodReference reference = method.getReference();
|
||||
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(']');
|
||||
}
|
||||
|
||||
private void print(PropertyGet node) throws IOException {
|
||||
public void print(PropertyGet node) throws IOException {
|
||||
print(node.getLeft(), PRECEDENCE_MEMBER);
|
||||
writer.append('.');
|
||||
var oldRootScope = rootScope;
|
||||
|
@ -651,8 +651,8 @@ public class AstWriter {
|
|||
writer.append(node.getQuoteCharacter());
|
||||
}
|
||||
|
||||
private void print(Name node, int precedence) throws IOException {
|
||||
if (rootScope) {
|
||||
public void print(Name node, int precedence) throws IOException {
|
||||
if (rootScope && node.getDefiningScope() == null) {
|
||||
var alias = nameMap.get(node.getIdentifier());
|
||||
if (alias == null) {
|
||||
if (globalNameWriter != null) {
|
||||
|
@ -667,6 +667,10 @@ public class AstWriter {
|
|||
}
|
||||
}
|
||||
|
||||
protected final boolean isRootScope() {
|
||||
return rootScope;
|
||||
}
|
||||
|
||||
private void print(RegExpLiteral node) throws IOException {
|
||||
writer.append('/').append(node.getValue()).append('/').append(node.getFlags());
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public class RuntimeRenderer {
|
|||
AstRoot ast = parseRuntime(name);
|
||||
ast.visit(new StringConstantElimination());
|
||||
new TemplatingAstTransformer(classSource).visit(ast);
|
||||
var astWriter = new TemplatingAstWriter(writer);
|
||||
var astWriter = new TemplatingAstWriter(writer, null, null);
|
||||
astWriter.hoist(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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
import org.mozilla.javascript.ast.ElementGet;
|
||||
import org.mozilla.javascript.ast.FunctionCall;
|
||||
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.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.rendering.AstWriter;
|
||||
|
@ -28,8 +31,13 @@ import org.teavm.model.MethodDescriptor;
|
|||
import org.teavm.model.MethodReference;
|
||||
|
||||
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));
|
||||
this.names = names;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -137,6 +145,19 @@ public class TemplatingAstWriter extends AstWriter {
|
|||
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 {
|
||||
var arg = call.getArguments().get(0);
|
||||
if (!(arg instanceof StringLiteral)) {
|
||||
|
@ -163,4 +184,22 @@ public class TemplatingAstWriter extends AstWriter {
|
|||
writer.append('.').appendField(new FieldReference(className, fieldName));
|
||||
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