mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
jso: support varargs in exported classes and modules
This commit is contained in:
parent
abb1ea0070
commit
931f0f1f4a
|
@ -15,15 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl;
|
package org.teavm.classlib.impl;
|
||||||
|
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.MethodContributorContext;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.platform.PlatformAnnotationProvider;
|
import org.teavm.platform.PlatformAnnotationProvider;
|
||||||
|
|
||||||
public class AnnotationVirtualMethods implements VirtualMethodContributor {
|
public class AnnotationVirtualMethods implements MethodContributor {
|
||||||
@Override
|
@Override
|
||||||
public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) {
|
public boolean isContributing(MethodContributorContext context, MethodReference methodRef) {
|
||||||
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
|
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
|
||||||
if (cls == null || !cls.getInterfaces().contains(PlatformAnnotationProvider.class.getName())) {
|
if (cls == null || !cls.getInterfaces().contains(PlatformAnnotationProvider.class.getName())) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -60,8 +60,8 @@ import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.MethodContributorContext;
|
||||||
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||||
import org.teavm.cache.EmptyMethodNodeCache;
|
import org.teavm.cache.EmptyMethodNodeCache;
|
||||||
import org.teavm.cache.MethodNodeCache;
|
import org.teavm.cache.MethodNodeCache;
|
||||||
|
@ -122,7 +122,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private DebugInformationEmitter debugEmitter;
|
private DebugInformationEmitter debugEmitter;
|
||||||
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
private MethodNodeCache astCache = EmptyMethodNodeCache.INSTANCE;
|
||||||
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
private final Set<MethodReference> asyncMethods = new HashSet<>();
|
||||||
private List<VirtualMethodContributor> customVirtualMethods = new ArrayList<>();
|
private List<MethodContributor> customVirtualMethods = new ArrayList<>();
|
||||||
|
private List<MethodContributor> forcedFunctionMethods = new ArrayList<>();
|
||||||
private boolean strict;
|
private boolean strict;
|
||||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
||||||
|
@ -366,11 +367,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
debugEmitterToUse = new DummyDebugInformationEmitter();
|
debugEmitterToUse = new DummyDebugInformationEmitter();
|
||||||
}
|
}
|
||||||
|
|
||||||
var virtualMethodContributorContext = new VirtualMethodContributorContextImpl(classes);
|
var methodContributorContext = new MethodContributorContextImpl(classes);
|
||||||
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
||||||
controller.getUnprocessedClassSource(), classes,
|
controller.getUnprocessedClassSource(), classes,
|
||||||
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
||||||
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m),
|
controller.getDependencyInfo(),
|
||||||
|
m -> isVirtual(methodContributorContext, m),
|
||||||
|
m -> isForcedFunction(methodContributorContext, m),
|
||||||
controller.getClassInitializerInfo(), strict
|
controller.getClassInitializerInfo(), strict
|
||||||
) {
|
) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -804,31 +807,45 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addVirtualMethods(VirtualMethodContributor virtualMethods) {
|
public void addVirtualMethods(MethodContributor virtualMethods) {
|
||||||
customVirtualMethods.add(virtualMethods);
|
customVirtualMethods.add(virtualMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addForcedFunctionMethods(MethodContributor forcedFunctionMethods) {
|
||||||
|
this.forcedFunctionMethods.add(forcedFunctionMethods);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAsyncSupported() {
|
public boolean isAsyncSupported() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isVirtual(VirtualMethodContributorContext context, MethodReference method) {
|
private boolean isVirtual(MethodContributorContext context, MethodReference method) {
|
||||||
if (controller.isVirtual(method)) {
|
if (controller.isVirtual(method)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (VirtualMethodContributor predicate : customVirtualMethods) {
|
for (MethodContributor predicate : customVirtualMethods) {
|
||||||
if (predicate.isVirtual(context, method)) {
|
if (predicate.isContributing(context, method)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class VirtualMethodContributorContextImpl implements VirtualMethodContributorContext {
|
private boolean isForcedFunction(MethodContributorContext context, MethodReference method) {
|
||||||
|
for (var predicate : forcedFunctionMethods) {
|
||||||
|
if (predicate.isContributing(context, method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodContributorContextImpl implements MethodContributorContext {
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
|
|
||||||
VirtualMethodContributorContextImpl(ClassReaderSource classSource) {
|
MethodContributorContextImpl(ClassReaderSource classSource) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.teavm.backend.javascript;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.vm.spi.RendererListener;
|
import org.teavm.vm.spi.RendererListener;
|
||||||
import org.teavm.vm.spi.TeaVMHostExtension;
|
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||||
|
@ -34,5 +34,7 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension {
|
||||||
|
|
||||||
void add(RendererListener listener);
|
void add(RendererListener listener);
|
||||||
|
|
||||||
void addVirtualMethods(VirtualMethodContributor virtualMethods);
|
void addVirtualMethods(MethodContributor virtualMethods);
|
||||||
|
|
||||||
|
void addForcedFunctionMethods(MethodContributor forcedFunctionMethods);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,12 +93,16 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderParameters(MethodReference reference, Set<ElementModifier> modifiers) {
|
public void renderParameters(MethodReference reference, Set<ElementModifier> modifiers) {
|
||||||
|
renderParameters(reference, modifiers, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderParameters(MethodReference reference, Set<ElementModifier> modifiers, boolean forceParentheses) {
|
||||||
int startParam = 0;
|
int startParam = 0;
|
||||||
if (modifiers.contains(ElementModifier.STATIC)) {
|
if (modifiers.contains(ElementModifier.STATIC)) {
|
||||||
startParam = 1;
|
startParam = 1;
|
||||||
}
|
}
|
||||||
var count = reference.parameterCount() - startParam + 1;
|
var count = reference.parameterCount() - startParam + 1;
|
||||||
if (count != 1) {
|
if (count != 1 || forceParentheses) {
|
||||||
writer.append("(");
|
writer.append("(");
|
||||||
}
|
}
|
||||||
for (int i = startParam; i <= reference.parameterCount(); ++i) {
|
for (int i = startParam; i <= reference.parameterCount(); ++i) {
|
||||||
|
@ -107,7 +111,7 @@ public class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
|
||||||
}
|
}
|
||||||
writer.append(statementRenderer.variableName(i));
|
writer.append(statementRenderer.variableName(i));
|
||||||
}
|
}
|
||||||
if (count != 1) {
|
if (count != 1 || forceParentheses) {
|
||||||
writer.append(")");
|
writer.append(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,8 +372,13 @@ public class Renderer implements RenderingManager {
|
||||||
if (!filterMethod(method)) {
|
if (!filterMethod(method)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writer.startVariableDeclaration();
|
var isFunction = context.isForcedFunction(method.getReference());
|
||||||
renderBody(method, decompiler);
|
if (isFunction) {
|
||||||
|
writer.startFunctionDeclaration();
|
||||||
|
} else {
|
||||||
|
writer.startVariableDeclaration();
|
||||||
|
}
|
||||||
|
renderBody(method, decompiler, isFunction);
|
||||||
writer.endDeclaration();
|
writer.endDeclaration();
|
||||||
if (needsInitializers && !method.hasModifier(ElementModifier.STATIC)
|
if (needsInitializers && !method.hasModifier(ElementModifier.STATIC)
|
||||||
&& method.getName().equals("<init>")) {
|
&& method.getName().equals("<init>")) {
|
||||||
|
@ -381,7 +386,6 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
writer.emitClass(null);
|
writer.emitClass(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +785,12 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitVirtualFunctionWrapper(MethodReference method) {
|
private void emitVirtualFunctionWrapper(MethodReference method) {
|
||||||
if (method.parameterCount() <= 4) {
|
var forced = context.isForcedFunction(method);
|
||||||
|
if (forced) {
|
||||||
|
writer.appendFunction("$rt_wrapFunctionVararg").append("(").appendMethod(method).append(")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (method.parameterCount() <= 4 && !forced) {
|
||||||
writer.appendFunction("$rt_wrapFunction" + method.parameterCount());
|
writer.appendFunction("$rt_wrapFunction" + method.parameterCount());
|
||||||
writer.append("(").appendMethod(method).append(")");
|
writer.append("(").appendMethod(method).append(")");
|
||||||
return;
|
return;
|
||||||
|
@ -810,22 +819,22 @@ public class Renderer implements RenderingManager {
|
||||||
writer.append(");").ws().append("}");
|
writer.append(");").ws().append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderBody(MethodHolder method, Decompiler decompiler) {
|
private void renderBody(MethodHolder method, Decompiler decompiler, boolean isFunction) {
|
||||||
MethodReference ref = method.getReference();
|
MethodReference ref = method.getReference();
|
||||||
writer.emitMethod(ref.getDescriptor());
|
writer.emitMethod(ref.getDescriptor());
|
||||||
|
|
||||||
writer.appendMethod(ref);
|
writer.appendMethod(ref);
|
||||||
if (method.hasModifier(ElementModifier.NATIVE)) {
|
if (method.hasModifier(ElementModifier.NATIVE)) {
|
||||||
renderNativeBody(method, classSource);
|
renderNativeBody(method, classSource, isFunction);
|
||||||
} else {
|
} else {
|
||||||
renderRegularBody(method, decompiler);
|
renderRegularBody(method, decompiler, isFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("}");
|
||||||
writer.emitMethod(null);
|
writer.emitMethod(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderNativeBody(MethodHolder method, ClassReaderSource classes) {
|
private void renderNativeBody(MethodHolder method, ClassReaderSource classes, boolean isFunction) {
|
||||||
var reference = method.getReference();
|
var reference = method.getReference();
|
||||||
var generator = generators.get(reference);
|
var generator = generators.get(reference);
|
||||||
if (generator == null) {
|
if (generator == null) {
|
||||||
|
@ -841,7 +850,7 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
var async = asyncMethods.contains(reference);
|
var async = asyncMethods.contains(reference);
|
||||||
renderMethodPrologue(reference, method.getModifiers());
|
renderMethodPrologue(reference, method.getModifiers(), isFunction);
|
||||||
methodBodyRenderer.renderNative(generator, async, reference);
|
methodBodyRenderer.renderNative(generator, async, reference);
|
||||||
threadLibraryUsed |= methodBodyRenderer.isThreadLibraryUsed();
|
threadLibraryUsed |= methodBodyRenderer.isThreadLibraryUsed();
|
||||||
}
|
}
|
||||||
|
@ -895,7 +904,7 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRegularBody(MethodHolder method, Decompiler decompiler) {
|
private void renderRegularBody(MethodHolder method, Decompiler decompiler, boolean isFunction) {
|
||||||
MethodReference reference = method.getReference();
|
MethodReference reference = method.getReference();
|
||||||
MethodNode node;
|
MethodNode node;
|
||||||
var async = asyncMethods.contains(reference);
|
var async = asyncMethods.contains(reference);
|
||||||
|
@ -907,14 +916,17 @@ public class Renderer implements RenderingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
methodBodyRenderer.setCurrentMethod(node);
|
methodBodyRenderer.setCurrentMethod(node);
|
||||||
renderMethodPrologue(method.getReference(), method.getModifiers());
|
renderMethodPrologue(method.getReference(), method.getModifiers(), isFunction);
|
||||||
methodBodyRenderer.render(node, async);
|
methodBodyRenderer.render(node, async);
|
||||||
threadLibraryUsed |= methodBodyRenderer.isThreadLibraryUsed();
|
threadLibraryUsed |= methodBodyRenderer.isThreadLibraryUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderMethodPrologue(MethodReference reference, Set<ElementModifier> modifier) {
|
private void renderMethodPrologue(MethodReference reference, Set<ElementModifier> modifier, boolean isFunction) {
|
||||||
methodBodyRenderer.renderParameters(reference, modifier);
|
methodBodyRenderer.renderParameters(reference, modifier, isFunction);
|
||||||
writer.sameLineWs().append("=>").ws().append("{").indent().softNewLine();
|
if (!isFunction) {
|
||||||
|
writer.sameLineWs().append("=>");
|
||||||
|
}
|
||||||
|
writer.ws().append("{").indent().softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) {
|
private AstCacheEntry decompileRegular(Decompiler decompiler, MethodHolder method) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ public abstract class RenderingContext {
|
||||||
private NamingStrategy naming;
|
private NamingStrategy naming;
|
||||||
private DependencyInfo dependencyInfo;
|
private DependencyInfo dependencyInfo;
|
||||||
private Predicate<MethodReference> virtualPredicate;
|
private Predicate<MethodReference> virtualPredicate;
|
||||||
|
private Predicate<MethodReference> forcedFunctionPredicate;
|
||||||
private final Map<String, Integer> stringPoolMap = new HashMap<>();
|
private final Map<String, Integer> stringPoolMap = new HashMap<>();
|
||||||
private final List<String> stringPool = new ArrayList<>();
|
private final List<String> stringPool = new ArrayList<>();
|
||||||
private final List<String> readonlyStringPool = Collections.unmodifiableList(stringPool);
|
private final List<String> readonlyStringPool = Collections.unmodifiableList(stringPool);
|
||||||
|
@ -63,7 +64,9 @@ public abstract class RenderingContext {
|
||||||
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
|
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
|
||||||
ClassLoader classLoader, ServiceRepository services, Properties properties,
|
ClassLoader classLoader, ServiceRepository services, Properties properties,
|
||||||
NamingStrategy naming, DependencyInfo dependencyInfo,
|
NamingStrategy naming, DependencyInfo dependencyInfo,
|
||||||
Predicate<MethodReference> virtualPredicate, ClassInitializerInfo classInitializerInfo,
|
Predicate<MethodReference> virtualPredicate,
|
||||||
|
Predicate<MethodReference> forcedFunctionPredicate,
|
||||||
|
ClassInitializerInfo classInitializerInfo,
|
||||||
boolean strict) {
|
boolean strict) {
|
||||||
this.debugEmitter = debugEmitter;
|
this.debugEmitter = debugEmitter;
|
||||||
this.initialClassSource = initialClassSource;
|
this.initialClassSource = initialClassSource;
|
||||||
|
@ -74,6 +77,7 @@ public abstract class RenderingContext {
|
||||||
this.naming = naming;
|
this.naming = naming;
|
||||||
this.dependencyInfo = dependencyInfo;
|
this.dependencyInfo = dependencyInfo;
|
||||||
this.virtualPredicate = virtualPredicate;
|
this.virtualPredicate = virtualPredicate;
|
||||||
|
this.forcedFunctionPredicate = forcedFunctionPredicate;
|
||||||
this.classInitializerInfo = classInitializerInfo;
|
this.classInitializerInfo = classInitializerInfo;
|
||||||
this.strict = strict;
|
this.strict = strict;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +122,10 @@ public abstract class RenderingContext {
|
||||||
return virtualPredicate.test(method);
|
return virtualPredicate.test(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isForcedFunction(MethodReference method) {
|
||||||
|
return forcedFunctionPredicate.test(method);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDynamicInitializer(String className) {
|
public boolean isDynamicInitializer(String className) {
|
||||||
return classInitializerInfo.isDynamicInitializer(className);
|
return classInitializerInfo.isDynamicInitializer(className);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,6 @@ package org.teavm.backend.javascript.spi;
|
||||||
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public interface VirtualMethodContributor {
|
public interface MethodContributor {
|
||||||
boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef);
|
boolean isContributing(MethodContributorContext context, MethodReference methodRef);
|
||||||
}
|
}
|
|
@ -17,6 +17,6 @@ package org.teavm.backend.javascript.spi;
|
||||||
|
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
|
||||||
public interface VirtualMethodContributorContext {
|
public interface MethodContributorContext {
|
||||||
ClassReaderSource getClassSource();
|
ClassReaderSource getClassSource();
|
||||||
}
|
}
|
|
@ -119,4 +119,12 @@ public interface ClassReaderSource {
|
||||||
default Optional<Boolean> isSuperType(String superType, String subType) {
|
default Optional<Boolean> isSuperType(String superType, String subType) {
|
||||||
return ClassReaderSourceHelper.isSuperType(this, superType, subType);
|
return ClassReaderSourceHelper.isSuperType(this, superType, subType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default MethodReader getMethod(MethodReference ref) {
|
||||||
|
var cls = get(ref.getClassName());
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return cls.getMethod(ref.getDescriptor());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,12 @@ let $rt_wrapFunction3 = f => function(p1, p2, p3) {
|
||||||
let $rt_wrapFunction4 = f => function(p1, p2, p3, p4) {
|
let $rt_wrapFunction4 = f => function(p1, p2, p3, p4) {
|
||||||
return f(this, p1, p2, p3, p4);
|
return f(this, p1, p2, p3, p4);
|
||||||
}
|
}
|
||||||
|
let $rt_wrapFunctionVararg = f => function() {
|
||||||
|
let array = new teavm_globals.Array();
|
||||||
|
array.push(this);
|
||||||
|
Array.prototype.push.apply(array, arguments);
|
||||||
|
return f.apply(null, array);
|
||||||
|
}
|
||||||
let $rt_threadStarter = f => function() {
|
let $rt_threadStarter = f => function() {
|
||||||
let args = teavm_globals.Array.prototype.slice.apply(arguments);
|
let args = teavm_globals.Array.prototype.slice.apply(arguments);
|
||||||
$rt_startThread(function() {
|
$rt_startThread(function() {
|
||||||
|
@ -111,3 +117,5 @@ let $rt_setThread = t => {
|
||||||
let $rt_apply = (instance, method, args) => instance[method].apply(instance, args);
|
let $rt_apply = (instance, method, args) => instance[method].apply(instance, args);
|
||||||
|
|
||||||
let $rt_apply_topLevel = (method, args) => method.apply(null, args);
|
let $rt_apply_topLevel = (method, args) => method.apply(null, args);
|
||||||
|
|
||||||
|
let $rt_skip = (array, count) => count === 0 ? array : Array.prototype.slice.call(array, count);
|
|
@ -716,4 +716,8 @@ final class JS {
|
||||||
@InjectedBy(JSNativeInjector.class)
|
@InjectedBy(JSNativeInjector.class)
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
public static native JSObject throwCCEIfFalse(boolean value, JSObject o);
|
public static native JSObject throwCCEIfFalse(boolean value, JSObject o);
|
||||||
|
|
||||||
|
@InjectedBy(JSNativeInjector.class)
|
||||||
|
@NoSideEffects
|
||||||
|
public static native JSObject argumentsBeginningAt(int index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@ import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.rendering.RenderingManager;
|
import org.teavm.backend.javascript.rendering.RenderingManager;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.MethodContributorContext;
|
||||||
import org.teavm.jso.JSClass;
|
import org.teavm.jso.JSClass;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
@ -35,7 +35,7 @@ import org.teavm.model.MethodReference;
|
||||||
import org.teavm.vm.BuildTarget;
|
import org.teavm.vm.BuildTarget;
|
||||||
import org.teavm.vm.spi.RendererListener;
|
import org.teavm.vm.spi.RendererListener;
|
||||||
|
|
||||||
class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
|
class JSAliasRenderer implements RendererListener, MethodContributor {
|
||||||
private static String variableChars = "abcdefghijklmnopqrstuvwxyz";
|
private static String variableChars = "abcdefghijklmnopqrstuvwxyz";
|
||||||
private SourceWriter writer;
|
private SourceWriter writer;
|
||||||
private ListableClassReaderSource classSource;
|
private ListableClassReaderSource classSource;
|
||||||
|
@ -389,7 +389,7 @@ class JSAliasRenderer implements RendererListener, VirtualMethodContributor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) {
|
public boolean isContributing(MethodContributorContext context, MethodReference methodRef) {
|
||||||
ClassReader classReader = context.getClassSource().get(methodRef.getClassName());
|
ClassReader classReader = context.getClassSource().get(methodRef.getClassName());
|
||||||
if (classReader == null) {
|
if (classReader == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.impl;
|
||||||
|
|
||||||
|
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||||
|
import org.teavm.backend.javascript.spi.MethodContributorContext;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class JSExportedMethodAsFunction implements MethodContributor {
|
||||||
|
@Override
|
||||||
|
public boolean isContributing(MethodContributorContext context, MethodReference methodRef) {
|
||||||
|
var method = context.getClassSource().getMethod(methodRef);
|
||||||
|
if (method != null) {
|
||||||
|
if (method.getAnnotations().get(JSVararg.class.getName()) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,6 +131,8 @@ final class JSMethods {
|
||||||
JSObject.class, boolean.class);
|
JSObject.class, boolean.class);
|
||||||
public static final MethodReference THROW_CCE_IF_FALSE = new MethodReference(JS.class, "throwCCEIfFalse",
|
public static final MethodReference THROW_CCE_IF_FALSE = new MethodReference(JS.class, "throwCCEIfFalse",
|
||||||
boolean.class, JSObject.class, JSObject.class);
|
boolean.class, JSObject.class, JSObject.class);
|
||||||
|
public static final MethodReference ARGUMENTS_BEGINNING_AT = new MethodReference(JS.class,
|
||||||
|
"argumentsBeginningAt", int.class, JSObject.class);
|
||||||
|
|
||||||
public static final ValueType JS_OBJECT = ValueType.object(JSObject.class.getName());
|
public static final ValueType JS_OBJECT = ValueType.object(JSObject.class.getName());
|
||||||
public static final ValueType OBJECT = ValueType.object("java.lang.Object");
|
public static final ValueType OBJECT = ValueType.object("java.lang.Object");
|
||||||
|
|
|
@ -237,6 +237,12 @@ public class JSNativeInjector implements Injector, DependencyPlugin {
|
||||||
writer.append(")");
|
writer.append(")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "argumentsBeginningAt": {
|
||||||
|
writer.appendFunction("$rt_skip").append("(arguments,").ws();
|
||||||
|
context.writeExpr(context.getArgument(0), Precedence.min());
|
||||||
|
writer.append(")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (methodRef.getName().startsWith("unwrap")) {
|
if (methodRef.getName().startsWith("unwrap")) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||||
jsHost.addInjectorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(),
|
jsHost.addInjectorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(),
|
||||||
DynamicInjector.class.getName()));
|
DynamicInjector.class.getName()));
|
||||||
jsHost.addVirtualMethods(aliasRenderer);
|
jsHost.addVirtualMethods(aliasRenderer);
|
||||||
|
jsHost.addForcedFunctionMethods(new JSExportedMethodAsFunction());
|
||||||
|
|
||||||
JSExceptionsGenerator exceptionsGenerator = new JSExceptionsGenerator();
|
JSExceptionsGenerator exceptionsGenerator = new JSExceptionsGenerator();
|
||||||
jsHost.add(new MethodReference(JSExceptions.class, "getJavaException", JSObject.class, Throwable.class),
|
jsHost.add(new MethodReference(JSExceptions.class, "getJavaException", JSObject.class, Throwable.class),
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.teavm.model.Program;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
|
||||||
|
@ -111,13 +112,18 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
private void exposeMethods(ClassHolder classHolder, ExposedClass classToExpose, Diagnostics diagnostics,
|
private void exposeMethods(ClassHolder classHolder, ExposedClass classToExpose, Diagnostics diagnostics,
|
||||||
MethodReference functorMethod) {
|
MethodReference functorMethod) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (MethodDescriptor method : classToExpose.methods.keySet()) {
|
for (var entry : classToExpose.methods.entrySet()) {
|
||||||
|
var method = entry.getKey();
|
||||||
|
var export = entry.getValue();
|
||||||
MethodReference methodRef = new MethodReference(classHolder.getName(), method);
|
MethodReference methodRef = new MethodReference(classHolder.getName(), method);
|
||||||
CallLocation callLocation = new CallLocation(methodRef);
|
CallLocation callLocation = new CallLocation(methodRef);
|
||||||
|
|
||||||
ValueType[] exportedMethodSignature = Arrays.stream(method.getSignature())
|
var paramCount = method.parameterCount();
|
||||||
.map(type -> ValueType.object(JSObject.class.getName()))
|
if (export.vararg) {
|
||||||
.toArray(ValueType[]::new);
|
--paramCount;
|
||||||
|
}
|
||||||
|
var exportedMethodSignature = new ValueType[paramCount + 1];
|
||||||
|
Arrays.fill(exportedMethodSignature, JSMethods.JS_OBJECT);
|
||||||
MethodDescriptor exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
MethodDescriptor exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
||||||
exportedMethodSignature);
|
exportedMethodSignature);
|
||||||
MethodHolder exportedMethod = new MethodHolder(exportedMethodDesc);
|
MethodHolder exportedMethod = new MethodHolder(exportedMethodDesc);
|
||||||
|
@ -134,10 +140,15 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
variablesToPass[i] = program.createVariable();
|
variablesToPass[i] = program.createVariable();
|
||||||
}
|
}
|
||||||
|
if (export.vararg) {
|
||||||
|
transformVarargParam(variablesToPass, program, marshallInstructions, exportedMethod, 1);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
|
var byRef = i == method.parameterCount() - 1 && export.vararg
|
||||||
|
&& typeHelper.isSupportedByRefType(method.parameterType(i));
|
||||||
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
||||||
method.parameterType(i), false, true);
|
method.parameterType(i), byRef, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
basicBlock.addAll(marshallInstructions);
|
basicBlock.addAll(marshallInstructions);
|
||||||
|
@ -161,8 +172,6 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
basicBlock.add(exit);
|
basicBlock.add(exit);
|
||||||
|
|
||||||
classHolder.addMethod(exportedMethod);
|
classHolder.addMethod(exportedMethod);
|
||||||
|
|
||||||
var export = classToExpose.methods.get(method);
|
|
||||||
exportedMethod.getAnnotations().add(createExportAnnotation(export));
|
exportedMethod.getAnnotations().add(createExportAnnotation(export));
|
||||||
|
|
||||||
if (methodRef.equals(functorMethod)) {
|
if (methodRef.equals(functorMethod)) {
|
||||||
|
@ -179,10 +188,14 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var paramCount = method.parameterCount();
|
||||||
|
var vararg = method.hasModifier(ElementModifier.VARARGS);
|
||||||
|
if (vararg) {
|
||||||
|
--paramCount;
|
||||||
|
}
|
||||||
var callLocation = new CallLocation(method.getReference());
|
var callLocation = new CallLocation(method.getReference());
|
||||||
var exportedMethodSignature = Arrays.stream(method.getSignature())
|
var exportedMethodSignature = new ValueType[paramCount + 1];
|
||||||
.map(type -> ValueType.object(JSObject.class.getName()))
|
Arrays.fill(exportedMethodSignature, JSMethods.JS_OBJECT);
|
||||||
.toArray(ValueType[]::new);
|
|
||||||
var exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
var exportedMethodDesc = new MethodDescriptor(method.getName() + "$exported$" + index++,
|
||||||
exportedMethodSignature);
|
exportedMethodSignature);
|
||||||
var exportedMethod = new MethodHolder(exportedMethodDesc);
|
var exportedMethod = new MethodHolder(exportedMethodDesc);
|
||||||
|
@ -200,10 +213,15 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
variablesToPass[i] = program.createVariable();
|
variablesToPass[i] = program.createVariable();
|
||||||
}
|
}
|
||||||
|
if (vararg) {
|
||||||
|
transformVarargParam(variablesToPass, program, marshallInstructions, exportedMethod, 0);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
|
var byRef = i == method.parameterCount() - 1 && vararg
|
||||||
|
&& typeHelper.isSupportedByRefType(method.parameterType(i));
|
||||||
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
variablesToPass[i] = marshaller.unwrapReturnValue(callLocation, variablesToPass[i],
|
||||||
method.parameterType(i), false, true);
|
method.parameterType(i), byRef, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
basicBlock.addAll(marshallInstructions);
|
basicBlock.addAll(marshallInstructions);
|
||||||
|
@ -232,6 +250,25 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void transformVarargParam(Variable[] variablesToPass, Program program,
|
||||||
|
List<Instruction> instructions, MethodHolder method, int additionalSkip) {
|
||||||
|
var last = variablesToPass.length - 1;
|
||||||
|
|
||||||
|
var lastConstant = new IntegerConstantInstruction();
|
||||||
|
lastConstant.setReceiver(program.createVariable());
|
||||||
|
lastConstant.setConstant(last + additionalSkip);
|
||||||
|
instructions.add(lastConstant);
|
||||||
|
|
||||||
|
var extractVarargs = new InvokeInstruction();
|
||||||
|
extractVarargs.setType(InvocationType.SPECIAL);
|
||||||
|
extractVarargs.setMethod(JSMethods.ARGUMENTS_BEGINNING_AT);
|
||||||
|
extractVarargs.setArguments(lastConstant.getReceiver());
|
||||||
|
extractVarargs.setReceiver(variablesToPass[last]);
|
||||||
|
instructions.add(extractVarargs);
|
||||||
|
|
||||||
|
method.getAnnotations().add(new AnnotationHolder(JSVararg.class.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
private AnnotationHolder createExportAnnotation(MethodExport export) {
|
private AnnotationHolder createExportAnnotation(MethodExport export) {
|
||||||
String annotationName;
|
String annotationName;
|
||||||
switch (export.kind) {
|
switch (export.kind) {
|
||||||
|
@ -403,7 +440,7 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
name = method.getName();
|
name = method.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new MethodExport(name, kind);
|
return new MethodExport(name, kind, method.hasModifier(ElementModifier.VARARGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFunctorField(ClassHolder cls, MethodReference method) {
|
private void addFunctorField(ClassHolder cls, MethodReference method) {
|
||||||
|
@ -437,10 +474,12 @@ class JSObjectClassTransformer implements ClassHolderTransformer {
|
||||||
static class MethodExport {
|
static class MethodExport {
|
||||||
final String alias;
|
final String alias;
|
||||||
final MethodKind kind;
|
final MethodKind kind;
|
||||||
|
boolean vararg;
|
||||||
|
|
||||||
MethodExport(String alias, MethodKind kind) {
|
MethodExport(String alias, MethodKind kind, boolean vararg) {
|
||||||
this.alias = alias;
|
this.alias = alias;
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
|
this.vararg = vararg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,8 +121,6 @@ class JSTypeHelper {
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (itemType instanceof ValueType.Object) {
|
|
||||||
return isJavaScriptClass(((ValueType.Object) itemType).getClassName());
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
26
jso/impl/src/main/java/org/teavm/jso/impl/JSVararg.java
Normal file
26
jso/impl/src/main/java/org/teavm/jso/impl/JSVararg.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.impl;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@interface JSVararg {
|
||||||
|
}
|
|
@ -18,8 +18,8 @@ package org.teavm.platform.plugin;
|
||||||
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.backend.javascript.spi.VirtualMethodContributor;
|
import org.teavm.backend.javascript.spi.MethodContributor;
|
||||||
import org.teavm.backend.javascript.spi.VirtualMethodContributorContext;
|
import org.teavm.backend.javascript.spi.MethodContributorContext;
|
||||||
import org.teavm.backend.javascript.templating.JavaScriptTemplate;
|
import org.teavm.backend.javascript.templating.JavaScriptTemplate;
|
||||||
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
@ -34,7 +34,7 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class AsyncMethodGenerator implements Generator, DependencyPlugin, VirtualMethodContributor {
|
public class AsyncMethodGenerator implements Generator, DependencyPlugin, MethodContributor {
|
||||||
private static final MethodDescriptor completeMethod = new MethodDescriptor("complete", Object.class, void.class);
|
private static final MethodDescriptor completeMethod = new MethodDescriptor("complete", Object.class, void.class);
|
||||||
private static final MethodDescriptor errorMethod = new MethodDescriptor("error", Throwable.class, void.class);
|
private static final MethodDescriptor errorMethod = new MethodDescriptor("error", Throwable.class, void.class);
|
||||||
private JavaScriptTemplate template;
|
private JavaScriptTemplate template;
|
||||||
|
@ -101,7 +101,7 @@ public class AsyncMethodGenerator implements Generator, DependencyPlugin, Virtua
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVirtual(VirtualMethodContributorContext context, MethodReference methodRef) {
|
public boolean isContributing(MethodContributorContext context, MethodReference methodRef) {
|
||||||
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
|
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
|
||||||
if (cls == null) {
|
if (cls == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -82,6 +82,11 @@ public class ExportTest {
|
||||||
testExport("exportClasses", ModuleWithExportedClasses.class);
|
testExport("exportClasses", ModuleWithExportedClasses.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void varargs() {
|
||||||
|
testExport("varargs", ModuleWithVararg.class);
|
||||||
|
}
|
||||||
|
|
||||||
private void testExport(String name, Class<?> moduleClass) {
|
private void testExport(String name, Class<?> moduleClass) {
|
||||||
if (!Boolean.parseBoolean(System.getProperty("teavm.junit.js", "true"))) {
|
if (!Boolean.parseBoolean(System.getProperty("teavm.junit.js", "true"))) {
|
||||||
return;
|
return;
|
||||||
|
@ -89,6 +94,7 @@ public class ExportTest {
|
||||||
try {
|
try {
|
||||||
var jsTarget = new JavaScriptTarget();
|
var jsTarget = new JavaScriptTarget();
|
||||||
jsTarget.setModuleType(JSModuleType.ES2015);
|
jsTarget.setModuleType(JSModuleType.ES2015);
|
||||||
|
jsTarget.setObfuscated(false);
|
||||||
var teavm = new TeaVMBuilder(jsTarget).build();
|
var teavm = new TeaVMBuilder(jsTarget).build();
|
||||||
var outputDir = new File(targetFile, name);
|
var outputDir = new File(targetFile, name);
|
||||||
teavm.installPlugins();
|
teavm.installPlugins();
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.export;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.teavm.jso.JSExport;
|
||||||
|
import org.teavm.jso.JSExportClasses;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSProperty;
|
||||||
|
import org.testng.util.Strings;
|
||||||
|
|
||||||
|
@JSExportClasses(ModuleWithVararg.A.class)
|
||||||
|
public class ModuleWithVararg {
|
||||||
|
private ModuleWithVararg() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExport
|
||||||
|
public static String strings(String... values) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("strings: ");
|
||||||
|
sb.append(Strings.join(", ", values));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExport
|
||||||
|
public static String prefixStrings(int a, String... values) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("strings(").append(a).append("): ");
|
||||||
|
sb.append(Strings.join(", ", values));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExport
|
||||||
|
public static String ints(int... values) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("ints: ");
|
||||||
|
sb.append(IntStream.of(values).mapToObj(Integer::toString).collect(Collectors.joining(", ")));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExport
|
||||||
|
public static String prefixInts(String prefix, int... values) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("ints(").append(prefix).append("): ");
|
||||||
|
sb.append(IntStream.of(values).mapToObj(Integer::toString).collect(Collectors.joining(", ")));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExport
|
||||||
|
public static String objects(ObjectWithString... values) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("objects: ");
|
||||||
|
sb.append(Stream.of(values).map(v -> v.getStringValue()).collect(Collectors.joining(", ")));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ObjectWithString extends JSObject {
|
||||||
|
@JSProperty
|
||||||
|
String getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class A {
|
||||||
|
@JSExport
|
||||||
|
public A() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSExport
|
||||||
|
public String strings(String... values) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.append("A.strings: ");
|
||||||
|
sb.append(Strings.join(", ", values));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
tests/src/test/resources/org/teavm/jso/export/varargs.js
Normal file
28
tests/src/test/resources/org/teavm/jso/export/varargs.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as varargs from '/tests/varargs/test.js';
|
||||||
|
|
||||||
|
export async function test() {
|
||||||
|
assertEquals("strings: a, b", varargs.strings("a", "b"));
|
||||||
|
assertEquals("strings(23): a, b", varargs.prefixStrings(23, "a", "b"));
|
||||||
|
assertEquals("ints: 23, 42", varargs.ints(23, 42));
|
||||||
|
assertEquals("ints(*): 23, 42", varargs.prefixInts("*", 23, 42));
|
||||||
|
assertEquals("objects: a, b", varargs.objects({ stringValue: "a" }, { stringValue: "b" }));
|
||||||
|
|
||||||
|
let obj = new varargs.A();
|
||||||
|
assertEquals("A.strings: a, b", obj.strings("a", "b"));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user