add $invoke capability

Brings dynamic invoke capability to def.js.Object.

It allows to dynamically call a method on an object for instance if
definition is missing or simply if it is not possible to do it
otherwise.
This commit is contained in:
Louis Grignon 2019-04-27 18:47:13 +02:00
parent 26beb8d8ce
commit 7f2000e03b
10 changed files with 155 additions and 74 deletions

View File

@ -234,6 +234,11 @@ public class Object {
*/
native public static Array<String> keys(java.lang.Object o);
/**
* Invoke method of given name on this object with given parameters.
*/
native public <T> T $invoke(java.lang.String methodName, java.lang.Object... args);
/**
* Gets the value for the given key. Generates <code>this[key]</code>.
*/

View File

@ -255,6 +255,11 @@ public class Object {
*/
native public java.lang.Boolean propertyIsEnumerable(double v);
/**
* Invoke method of given name on this object with given parameters.
*/
native public <T> T $invoke(java.lang.String methodName, java.lang.Object... args);
/**
* Gets the value for the given key. Generates <code>this[key]</code>.
*/

View File

@ -707,6 +707,8 @@ public final class Lang {
public static native <R> R await(Promise<R> promise);
public static native <R> R await(R promise);
public static native <R> def.js.Function async(def.js.Function function);
public static native <T> Promise<T> asyncReturn(T result);

View File

@ -234,7 +234,7 @@
<dependency>
<groupId>org.jsweet</groupId>
<artifactId>jsweet-core</artifactId>
<version>6</version>
<version>6.0.1-SNAPSHOT</version>
<scope>test</scope>
<optional>true</optional>
</dependency>

View File

@ -204,6 +204,8 @@ public abstract class JSweetConfig {
public static final String UNION_PACKAGE = UTIL_PACKAGE + ".union";
/** The constant for the Union core class full name. */
public static final String UNION_CLASS_NAME = UNION_PACKAGE + ".Union";
/** The constant for dynamic invoke function. */
public static final String INVOKE_FUCTION_NAME = "$invoke";
/** The constant for indexed access function. */
public static final String INDEXED_GET_FUCTION_NAME = "$get";
/** The constant for indexed assignment function. */

View File

@ -1620,6 +1620,10 @@ public class JSweetContext extends Context {
return false;
}
/**
* Returns true if given statement can be used in a Java object instantation block
* to be transformed to a JS Object Literal
*/
private static boolean isAllowedStatementInMap(JCStatement statement) {
if (statement instanceof JCExpressionStatement) {
JCExpressionStatement exprStat = (JCExpressionStatement) statement;

View File

@ -30,6 +30,7 @@ import static org.jsweet.JSweetConfig.INDEXED_GET_FUCTION_NAME;
import static org.jsweet.JSweetConfig.INDEXED_GET_STATIC_FUCTION_NAME;
import static org.jsweet.JSweetConfig.INDEXED_SET_FUCTION_NAME;
import static org.jsweet.JSweetConfig.INDEXED_SET_STATIC_FUCTION_NAME;
import static org.jsweet.JSweetConfig.INVOKE_FUCTION_NAME;
import static org.jsweet.JSweetConfig.LANG_PACKAGE;
import static org.jsweet.JSweetConfig.LANG_PACKAGE_ALT;
import static org.jsweet.JSweetConfig.UTIL_CLASSNAME;
@ -134,8 +135,7 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
/**
* Creates a root adapter (with no parent).
*
* @param context
* the transpilation context
* @param context the transpilation context
*/
public Java2TypeScriptAdapter(JSweetContext context) {
super(context);
@ -146,9 +146,9 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
* Creates a new adapter that will try delegate to the given parent adapter when
* not implementing its own behavior.
*
* @param parentAdapter
* cannot be null: if no parent you must use the
* {@link #AbstractPrinterAdapter(JSweetContext)} constructor
* @param parentAdapter cannot be null: if no parent you must use the
* {@link #AbstractPrinterAdapter(JSweetContext)}
* constructor
*/
public Java2TypeScriptAdapter(PrinterAdapter parent) {
super(parent);
@ -639,6 +639,33 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
if (targetMethodName != null) {
switch (targetMethodName) {
case INVOKE_FUCTION_NAME:
if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName)
|| DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) {
} else {
if (invocationElement.getArgumentCount() == 1) {
print("this[").print(invocationElement.getArguments().get(0)).print("]");
} else {
print(invocationElement.getArguments().get(0)).print("[")
.print(invocationElement.getArguments().get(1)).print("]");
}
}
print(invocationElement.getTargetExpression());
print("[");
print(invocationElement.getArgument(0));
print("]");
print("(");
List<ExtendedElement> arguments = invocationElement.getArguments();
int argCount = arguments.size();
for (int i = 1; i < argCount; i++) {
print(arguments.get(i));
if (i < argCount - 1) {
print(",");
}
}
print(")");
return true;
case INDEXED_GET_FUCTION_NAME:
if (isWithinGlobals(targetClassName)) {
if (invocationElement.getArgumentCount() == 1) {
@ -1327,8 +1354,7 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
}
/**
* @param enclosingElement
* is required for functional (ie dynamic) type mappings
* @param enclosingElement is required for functional (ie dynamic) type mappings
*/
public boolean substituteAndPrintType(ExtendedElement enclosingElement, TypeElement type) {
String typeFullName = type.toString();

View File

@ -22,22 +22,37 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.jsweet.test.transpiler.util.TranspilerTestRunner;
import org.jsweet.transpiler.*;
import org.jsweet.transpiler.extension.PrinterAdapter;
import org.jsweet.transpiler.model.ExtendedElement;
import org.jsweet.transpiler.model.MethodInvocationElement;
import org.jsweet.transpiler.JSweetProblem;
import org.jsweet.transpiler.ModuleKind;
import org.jsweet.transpiler.SourceFile;
import org.jsweet.transpiler.util.EvaluationResult;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import source.syntax.*;
import source.syntax.AnnotationQualifiedNames;
import source.syntax.Casts;
import source.syntax.DocComments;
import source.syntax.DynamicInvoke;
import source.syntax.FinalVariables;
import source.syntax.FinalVariablesRuntime;
import source.syntax.GlobalsCastMethod;
import source.syntax.GlobalsInvocation;
import source.syntax.Keywords;
import source.syntax.Labels;
import source.syntax.LambdaExpression;
import source.syntax.LambdasWithInterfaces;
import source.syntax.Literals;
import source.syntax.Looping;
import source.syntax.MemberReferences;
import source.syntax.QualifiedNames;
import source.syntax.References;
import source.syntax.SpecialFunctions;
import source.syntax.StatementsWithNoBlocks;
import source.syntax.SuperInvocation;
import source.syntax.ValidIndexedAccesses;
public class SyntaxTests extends AbstractTest {
@ -140,14 +155,17 @@ public class SyntaxTests extends AbstractTest {
}
@Ignore
@Test
public void testIndexedAccessInStaticScope() {
public void testDynamicInvoke() {
eval((logHandler, r) -> {
Assert.assertEquals("Wrong output value", "value", r.get("out_a"));
Assert.assertNull("Wrong output value", r.get("out_b"));
Assert.assertNull("var wasn't deleted", r.get("out_c"));
}, getSourceFile(IndexedAccessInStaticScope.class));
assertEquals(true, r.get("a_1"));
assertEquals(true, r.get("a"));
assertEquals(true, r.get("b"));
assertEquals(true, r.get("c"));
assertEquals(true, r.get("d"));
assertEquals(true, r.get("e"));
assertEquals("5;true;foo", r.get("f"));
}, getSourceFile(DynamicInvoke.class));
}
@Test

View File

@ -0,0 +1,70 @@
/*
* JSweet - http://www.jsweet.org
* Copyright (C) 2015 CINCHEO SAS <renaud.pawlak@cincheo.fr>
*
* 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 source.syntax;
import static jsweet.util.Lang.$export;
import static jsweet.util.Lang.object;
public class DynamicInvoke {
{
object(this).$invoke("a");
jsweet.util.Lang.object(this).$invoke("b");
}
public DynamicInvoke() {
object(this).$invoke("c");
jsweet.util.Lang.object(this).$invoke("d");
}
public void a_1() {
$export("a_1", "true");
}
public void a() {
object(this).$invoke("a_1");
$export("a", "true");
}
public void b() {
$export("b", "true");
}
public void c() {
$export("c", "true");
}
public void d() {
$export("d", "true");
}
public void e() {
$export("e", "true");
}
public void f(Integer i, Boolean b, String s) {
$export("f", i + ";" + b + ";" + s);
}
public static void main(String[] args) {
DynamicInvoke test = new DynamicInvoke();
object(test).$invoke("e");
object(test).$invoke("f", 5, new Boolean(true), "foo");
}
}

View File

@ -1,51 +0,0 @@
/*
* JSweet - http://www.jsweet.org
* Copyright (C) 2015 CINCHEO SAS <renaud.pawlak@cincheo.fr>
*
* 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 source.syntax;
public class IndexedAccessInStaticScope {
static {
// $get("a");
// $set("a", "value");
// jsweet.util.Globals.$get("a");
// jsweet.util.Globals.$set("a", "value");
}
public static void m() {
// $get("a");
// $set("a", "value");
// jsweet.util.Globals.$get("a");
// jsweet.util.Globals.$set("a", "value");
//
// $set("c", "i want to be deleted");
// $delete("c");
}
public static void main(String[] args) {
// m();
// $export("out_a", $get("a"));
// $export("out_b", $get("b"));
// $export("out_c", $get("c"));
}
}
class C {
public static void m() {
// $set("b", "value");
}
}