Skip to content

Commit

Permalink
Merge pull request #2081 from karatelabs/graal22
Browse files Browse the repository at this point in the history
JS engine refactor - graal upgrade - JS functions can be passed around safely
  • Loading branch information
ptrthomas authored Aug 2, 2022
2 parents 7e592e0 + 763590e commit 220c5a1
Show file tree
Hide file tree
Showing 25 changed files with 351 additions and 615 deletions.
10 changes: 10 additions & 0 deletions karate-core/src/main/java/com/intuit/karate/JsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ public static String toCsv(List<Map<String, Object>> list) {
return sw.toString();
}

public static Object shallowCopy(Object o) {
if (o instanceof List) {
return new ArrayList((List) o);
} else if (o instanceof Map) {
return new LinkedHashMap((Map) o);
} else {
return o;
}
}

public static Object deepCopy(Object o) {
// anti recursion / back-references
Set<Object> seen = Collections.newSetFromMap(new IdentityHashMap());
Expand Down
35 changes: 0 additions & 35 deletions karate-core/src/main/java/com/intuit/karate/core/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,8 @@
import com.intuit.karate.StringUtils;
import com.intuit.karate.driver.DockerTarget;
import com.intuit.karate.driver.Target;
import com.intuit.karate.graal.JsEngine;
import com.intuit.karate.graal.JsFunction;
import com.intuit.karate.http.Cookies;
import com.intuit.karate.http.HttpLogModifier;
import org.graalvm.polyglot.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -110,38 +107,6 @@ public Config() {
// zero arg constructor
}

private static Variable attach(Variable v, JsEngine je) {
if (v.isJsFunctionWrapper()) {
JsFunction jf = v.getValue();
Value attached = je.attachSource(jf.source);
return new Variable(attached);
} else {
return v;
}
}

private static Variable detach(Variable v) {
if (v.isJsFunction()) {
return new Variable(new JsFunction(v.getValue()));
} else {
return v;
}
}

protected void attach(JsEngine je) {
afterScenario = attach(afterScenario, je);
afterFeature = attach(afterFeature, je);
headers = attach(headers, je);
cookies = attach(cookies, je);
}

protected void detach() {
afterScenario = detach(afterScenario);
afterFeature = detach(afterFeature);
headers = detach(headers);
cookies = detach(cookies);
}

private static <T> T get(Map<String, Object> map, String key, T defaultValue) {
Object o = map.get(key);
return o == null ? defaultValue : (T) o;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public MockHandler(String prefix, List<Feature> features, Map<String, Object> ar
}
}
corsEnabled = corsEnabled || runtime.engine.getConfig().isCorsEnabled();
globals.putAll(runtime.engine.detachVariables());
globals.putAll(runtime.engine.shallowCloneVariables());
runtime.logger.info("mock server initialized: {}", feature);
this.features.put(feature, runtime);
}
Expand Down Expand Up @@ -187,7 +187,7 @@ public synchronized Response handle(Request req) { // note the [synchronized]
responseStatus = engine.vars.remove(ScenarioEngine.RESPONSE_STATUS);
responseHeaders = engine.vars.remove(ScenarioEngine.RESPONSE_HEADERS);
responseDelay = engine.vars.remove(RESPONSE_DELAY);
globals.putAll(engine.detachVariables());
globals.putAll(engine.shallowCloneVariables());
Response res = new Response(200);
if (result.isFailed()) {
response = new Variable(result.getError().getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import com.intuit.karate.http.HttpRequest;
import com.intuit.karate.http.HttpRequestBuilder;
import com.intuit.karate.http.ResourceType;
import com.intuit.karate.http.Response;
import com.intuit.karate.http.WebSocketClient;
import com.intuit.karate.http.WebSocketOptions;
import com.intuit.karate.shell.Command;
Expand Down Expand Up @@ -180,9 +179,8 @@ private static Object callSingleResult(ScenarioEngine engine, Object o) throws E
engine.logger.warn("callSingle() cached result is an exception");
throw (Exception) o;
}
// if we don't clone, an attach operation would update the tree within the cached value
// causing future cache hit + attach attempts to fail !
o = engine.recurseAndAttachAndShallowClone(o);
// shallow clone so that threads see the same data snapshot
o = JsonUtils.shallowCopy(o);
return JsValue.fromJava(o);
}

Expand Down Expand Up @@ -257,8 +255,7 @@ public Object callSingle(String fileName, Value arg) throws Exception {
engine.logger.warn("callSingleCache write failed, not json-like: {}", resultVar);
}
}
// functions have to be detached so that they can be re-hydrated in another js context
result = engine.recurseAndDetachAndShallowClone(resultVar.getValue());
result = resultVar.getValue();
}
CACHE.put(fileName, result);
engine.logger.info("<< lock released, cached callSingle: {}", fileName);
Expand Down Expand Up @@ -739,10 +736,10 @@ public Object repeat(int n, Value f) {
}
return new JsList(list);
}

public String responseHeader(String name) {
return getEngine().getResponse().getHeader(name);
}
}

// set multiple variables in one shot
public void set(Map<String, Object> map) {
Expand Down Expand Up @@ -879,12 +876,7 @@ public String toCsv(Object o) {
}

public Object toJava(Value value) {
if (value.canExecute()) {
JsEngine copy = getEngine().JS.copy();
return new JsLambda(copy.attach(value));
} else {
return new JsValue(value).getValue();
}
return new JsValue(value).getValue();
}

public File toJavaFile(String path) {
Expand Down Expand Up @@ -980,8 +972,7 @@ public WebSocketClient webSocket(String url, Value listener, Value value) {
if (listener == null || !listener.canExecute()) {
handler = m -> true;
} else {
JsEngine copy = engine.JS.copy();
handler = new JsLambda(copy.attach(listener));
handler = new JsLambda(listener);
}
WebSocketOptions options = new WebSocketOptions(url, value == null ? null : new JsValue(value).getValue());
options.setTextHandler(handler);
Expand All @@ -1002,8 +993,7 @@ public WebSocketClient webSocketBinary(String url, Value listener, Value value)
if (listener == null || !listener.canExecute()) {
handler = m -> true;
} else {
JsEngine copy = engine.JS.copy();
handler = new JsLambda(copy.attach(listener));
handler = new JsLambda(listener);
}
WebSocketOptions options = new WebSocketOptions(url, value == null ? null : new JsValue(value).getValue());
options.setBinaryHandler(handler);
Expand Down
Loading

0 comments on commit 220c5a1

Please sign in to comment.