Skip to content

Commit

Permalink
improve js engine eval-local routine
Browse files Browse the repository at this point in the history
and reuse for new server-context evalWith() function
  • Loading branch information
ptrthomas committed Mar 25, 2021
1 parent 340aea6 commit 80434d4
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 28 deletions.
31 changes: 29 additions & 2 deletions karate-core/src/main/java/com/intuit/karate/graal/JsEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
import com.intuit.karate.FileUtils;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Value;
Expand Down Expand Up @@ -144,10 +147,10 @@ public void putValue(String key, Value v) {
put(key, JsValue.toJava(v));
}
}

public Value attachSource(CharSequence source) {
Value value = evalForValue("(" + source + ")");
return attach(value);
return attach(value);
}

public Value attach(Value function) {
Expand All @@ -168,6 +171,30 @@ public JsValue execute(Value function, Object... args) {
return new JsValue(result);
}

public Value evalLocal(boolean returnValue, String src, Value value) {
return evalLocal(returnValue, src, value.getMemberKeys(), value::getMember);
}

public Value evalLocal(boolean returnValue, String src, Map<String, Object> variables) {
return evalLocal(returnValue, src, variables.keySet(), variables::get);
}

public Value evalLocal(boolean returnValue, String src, Set<String> names, Function<String, Object> getVariable) {
StringBuilder sb = new StringBuilder();
sb.append("(function(x){ ");
Map<String, Object> arg = new HashMap(names.size());
for (String name : names) {
sb.append("let ").append(name).append(" = x.").append(name).append("; ");
arg.put(name, getVariable.apply(name));
}
if (returnValue) {
sb.append("return ");
}
sb.append(src).append(" })");
Value function = evalForValue(sb.toString());
return function.execute(JsValue.fromJava(arg));
}

@Override
public String toString() {
return context.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class ServerContext implements ProxyObject {
private static final String READ = "read";
private static final String READ_AS_STRING = "readAsString";
private static final String EVAL = "eval";
private static final String EVAL_WITH = "evalWith";
private static final String UUID = "uuid";
private static final String REMOVE = "remove";
private static final String SWITCH = "switch";
Expand All @@ -69,7 +70,7 @@ public class ServerContext implements ProxyObject {
private static final String FROM_JSON = "fromJson";

private static final String[] KEYS = new String[]{
READ, READ_AS_STRING, EVAL, UUID, REMOVE, SWITCH, AJAX, HTTP, TRIGGER, AFTER_SETTLE, TO_JSON, TO_JSON_PRETTY, FROM_JSON};
READ, READ_AS_STRING, EVAL, EVAL_WITH, UUID, REMOVE, SWITCH, AJAX, HTTP, TRIGGER, AFTER_SETTLE, TO_JSON, TO_JSON_PRETTY, FROM_JSON};
private static final Set<String> KEY_SET = new HashSet(Arrays.asList(KEYS));
private static final JsArray KEY_ARRAY = new JsArray(KEYS);

Expand Down Expand Up @@ -173,15 +174,20 @@ public Object eval(String source) {
return RequestCycle.get().getEngine().evalForValue(source);
}

public Object evalWith(String source, Object o) {
Value value = Value.asValue(o);
return RequestCycle.get().getEngine().evalLocal(true, source, value);
}

public String toJson(Object o) {
Value value = Value.asValue(o);
return new JsValue(value).toJsonOrXmlString(false);
}

public String toJsonPretty(Object o) {
Value value = Value.asValue(o);
return new JsValue(value).toJsonOrXmlString(true);
}
}

public ServerConfig getConfig() {
return config;
Expand Down Expand Up @@ -283,13 +289,15 @@ public Object getMember(String key) {
case READ_AS_STRING:
return (Function<String, String>) this::readAsString;
case EVAL:
return (Function<String, Object>) this::eval;
return (Function<String, Object>) this::eval;
case EVAL_WITH:
return (BiFunction<String, Object, Object>) this::evalWith;
case UUID:
return UUID_FUNCTION;
case TO_JSON:
return (Function<Object, String>) this::toJson;
case TO_JSON_PRETTY:
return (Function<Object, String>) this::toJsonPretty;
return (Function<Object, String>) this::toJsonPretty;
case FROM_JSON:
return FROM_JSON_FUNCTION;
case REMOVE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public KaHxValsAttrProcessor(String dialectPrefix) {

@Override
protected void doProcess(ITemplateContext ctx, IProcessableElementTag tag, AttributeName an, String av, IElementTagStructureHandler sh) {
JsValue jv = TemplateEngineContext.get().eval("({" + av + "})", true);
JsValue jv = TemplateEngineContext.get().evalLocal("({" + av + "})", true);
if (!jv.isObject()) {
logger.warn("value did not evaluate to json: {}", av);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public KaScriptAttrProcessor(String dialectPrefix, ResourceResolver resourceReso
protected void doProcess(ITemplateContext ctx, IProcessableElementTag tag, AttributeName an, String av, IElementTagStructureHandler sh) {
InputStream is = resourceResolver.resolve(av).getStream();
String src = FileUtils.toString(is);
TemplateEngineContext.get().eval(src, false);
TemplateEngineContext.get().evalLocal(src, false);
sh.removeElement();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected void doProcess(ITemplateContext ctx, IModel model, IElementModelStruct
String text = StringUtils.trimToNull(((IText) event).getText());
if (text != null) {
if ("local".equals(scope)) {
TemplateEngineContext.get().eval(text, false);
TemplateEngineContext.get().evalLocal(text, false);
} else {
TemplateEngineContext.get().evalGlobal(text);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ protected void doProcess(ITemplateContext context, IProcessableElementTag tag, A
iterVarName = av.substring(0, pos).trim();
av = av.substring(pos + 1);
}
Object value = TemplateEngineContext.get().eval(av, true).getValue();
Object value = TemplateEngineContext.get().evalLocal(av, true).getValue();
structureHandler.iterateElement(iterVarName, null, value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public String getStringRepresentation() {

@Override
public Object execute(IExpressionContext context) {
return TemplateEngineContext.get().eval(input, true).getValue();
return TemplateEngineContext.get().evalLocal(input, true).getValue();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,9 @@ public JsValue evalGlobal(String src) {
return jsEngine.eval(src);
}

public JsValue eval(String src, boolean returnValue) {
StringBuilder sb = new StringBuilder();
sb.append("(function(x){ ");
Set<String> names = getVariableNames();
Map<String, Object> arg = new HashMap(names.size());
for (String name : getVariableNames()) {
sb.append("let ").append(name).append(" = x.").append(name).append("; ");
arg.put(name, getVariable(name));
}
if (returnValue) {
sb.append("return ");
}
sb.append(src).append(" })");
Value function = jsEngine.evalForValue(sb.toString());
Value result = function.execute(JsValue.fromJava(arg));
return new JsValue(result);
public JsValue evalLocal(String src, boolean returnValue) {
Value value = jsEngine.evalLocal(returnValue, src, getVariableNames(), this::getVariable);
return new JsValue(value);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,14 @@ void testEvalWithinFunction() {
Value result = function.execute(JsValue.fromJava(map));
assertEquals(result.asInt(), 3);
}

@Test
void testEvalLocal() {
Map<String, Object> map = new HashMap();
map.put("a", 1);
map.put("b", 2);
Value result = je.evalLocal(true, "a + b", map);
assertEquals(result.asInt(), 3);
}

}

0 comments on commit 80434d4

Please sign in to comment.