Skip to content

Commit

Permalink
Add invokedynamic instructions for a few more property operations
Browse files Browse the repository at this point in the history
Also make the DefaultLinker code have a bit less copy and paste
  • Loading branch information
gbrail committed Sep 24, 2024
1 parent 97970f9 commit 22df4ed
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1363,15 +1363,15 @@ private void generateExpression(Node node, Node parent) {
generateExpression(child.getNext(), node); // id
cfw.addALoad(contextLocal);
if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
addScriptRuntimeInvoke(
"getObjectIndex",
addDynamicInvoke(
"PROP:GETINDEX",
"(Ljava/lang/Object;D"
+ "Lorg/mozilla/javascript/Context;"
+ ")Ljava/lang/Object;");
} else {
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getObjectElem",
addDynamicInvoke(
"PROP:GETELEMENT",
"(Ljava/lang/Object;"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
Expand Down Expand Up @@ -1688,13 +1688,10 @@ private void generateYieldPoint(Node node, boolean exprContext) {
unnestedYieldCount++;
cfw.addALoad(variableObjectLocal);
cfw.add(ByteCode.SWAP);
cfw.addLoadConstant(nn);
cfw.add(ByteCode.SWAP);
cfw.addALoad(contextLocal);

addScriptRuntimeInvoke(
"setObjectProp",
"(Lorg/mozilla/javascript/Scriptable;Ljava/lang/String;Ljava/lang/Object;"
addDynamicInvoke(
"PROP:SET:" + nn,
"(Lorg/mozilla/javascript/Scriptable;Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;)Ljava/lang/Object;");
cfw.add(ByteCode.POP);

Expand Down Expand Up @@ -3867,14 +3864,12 @@ private void visitSetName(Node node, Node child) {
}
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addPush(name);
addScriptRuntimeInvoke(
"setName",
addDynamicInvoke(
"NAME:SET:" + name,
"(Lorg/mozilla/javascript/Scriptable;"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "Ljava/lang/String;"
+ ")Ljava/lang/Object;");
}

Expand All @@ -3886,14 +3881,12 @@ private void visitStrictSetName(Node node, Node child) {
}
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addPush(name);
addScriptRuntimeInvoke(
"strictSetName",
addDynamicInvoke(
"NAME:SETSTRICT:" + name,
"(Lorg/mozilla/javascript/Scriptable;"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ "Ljava/lang/String;"
+ ")Ljava/lang/Object;");
}

Expand All @@ -3904,13 +3897,11 @@ private void visitSetConst(Node node, Node child) {
child = child.getNext();
}
cfw.addALoad(contextLocal);
cfw.addPush(name);
addScriptRuntimeInvoke(
"setConst",
addDynamicInvoke(
"NAME:SETCONST:" + name,
"(Lorg/mozilla/javascript/Scriptable;"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ "Ljava/lang/String;"
+ ")Ljava/lang/Object;");
}

Expand Down Expand Up @@ -4096,8 +4087,8 @@ private void visitSetElem(int type, Node node, Node child) {
cfw.add(ByteCode.DUP2_X1);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getObjectIndex",
addDynamicInvoke(
"PROP:GETINDEX",
"(Ljava/lang/Object;D"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
Expand All @@ -4108,8 +4099,8 @@ private void visitSetElem(int type, Node node, Node child) {
cfw.add(ByteCode.DUP_X1);
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getObjectElem",
addDynamicInvoke(
"PROP:GETELEMENT",
"(Ljava/lang/Object;"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
Expand All @@ -4121,17 +4112,17 @@ private void visitSetElem(int type, Node node, Node child) {
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
if (indexIsNumber) {
addScriptRuntimeInvoke(
"setObjectIndex",
addDynamicInvoke(
"PROP:SETINDEX",
"(Ljava/lang/Object;"
+ "D"
+ "Ljava/lang/Object;"
+ "Lorg/mozilla/javascript/Context;"
+ "Lorg/mozilla/javascript/Scriptable;"
+ ")Ljava/lang/Object;");
} else {
addScriptRuntimeInvoke(
"setObjectElem",
addDynamicInvoke(
"PROP:SETELEMENT",
"(Ljava/lang/Object;"
+ "Ljava/lang/Object;"
+ "Ljava/lang/Object;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,36 +61,71 @@ private static Operation parseOperation(String name) throws NoSuchMethodExceptio
if ("PROP".equals(namespaceName)) {
switch (opName) {
case "GET":
// Get an object property with a constant name
return StandardOperation.GET
.withNamespace(StandardNamespace.PROPERTY)
.named(getNameSegment(tokens, name, 2));
case "GETNOWARN":
// Same with no warning of strict mode
return RhinoOperation.GETNOWARN
.withNamespace(StandardNamespace.PROPERTY)
.named(getNameSegment(tokens, name, 2));
case "GETWITHTHIS":
// Same but also return "this" so that it is found by "lastStoredScriptable"
return RhinoOperation.GETWITHTHIS
.withNamespace(StandardNamespace.PROPERTY)
.named(getNameSegment(tokens, name, 2));
case "GETELEMENT":
// Get the value of an element from a property that is on the stack,\
// as if using "[]" notation. Could be a String, number, or Symbol
return RhinoOperation.GETELEMENT.withNamespace(StandardNamespace.PROPERTY);
case "GETINDEX":
// Same but the value is definitely a numeric index
return RhinoOperation.GETINDEX.withNamespace(StandardNamespace.PROPERTY);
case "SET":
// Set an object property with a constant name
return StandardOperation.SET
.withNamespace(StandardNamespace.PROPERTY)
.named(getNameSegment(tokens, name, 2));
case "SETELEMENT":
// Set an object property as if by "[]", with a property on the stack
return RhinoOperation.SETELEMENT.withNamespace(StandardNamespace.PROPERTY);
case "SETINDEX":
// Same but the property name is definitely a number
return RhinoOperation.SETINDEX.withNamespace(StandardNamespace.PROPERTY);
}
} else if ("NAME".equals(namespaceName)) {
switch (opName) {
case "BIND":
// Bind a new variable to the context with a constant name
return RhinoOperation.BIND
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
case "GET":
// Get a variable from the context with a constant name
return StandardOperation.GET
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
case "GETWITHTHIS":
// Same but also return "this" so that it is found by "lastStoredScriptable"
return RhinoOperation.GETWITHTHIS
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
case "SET":
// Set an object in the context with a constant name
return StandardOperation.SET
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
case "SETSTRICT":
// Same but implement strict mode checks
return RhinoOperation.SETSTRICT
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
case "SETCONST":
// Same but try to set a constant
return RhinoOperation.SETCONST
.withNamespace(RhinoNamespace.NAME)
.named(getNameSegment(tokens, name, 2));
}
}

Expand All @@ -101,10 +136,9 @@ private static Operation parseOperation(String name) throws NoSuchMethodExceptio

// Given a list of name segments and a position, return the interned name at the
// specified position.
private static String getNameSegment(String[] segments, String name, int pos)
throws NoSuchMethodException {
private static String getNameSegment(String[] segments, String name, int pos) {
if (pos >= segments.length) {
throw new NoSuchMethodException(name);
return "";
}
// Because segments of operation names, especially property names, are essentially
// wired in to the bootstrapping result, interning works and has a big impact on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,25 @@ private GuardedInvocation getPropertyInvocation(
// Put the property name back in the right place in the parameter list
// so that we can invoke the operation normally.
if (StandardOperation.GET.equals(op)) {
tt = mType.insertParameterTypes(1, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "getObjectProp", tt);
mh = MethodHandles.insertArguments(mh, 1, name);
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "getObjectProp", 1, name);
} else if (RhinoOperation.GETNOWARN.equals(op)) {
tt = mType.insertParameterTypes(1, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "getObjectPropNoWarn", tt);
mh = MethodHandles.insertArguments(mh, 1, name);
mh =
bindStringParameter(
lookup, mType, ScriptRuntime.class, "getObjectPropNoWarn", 1, name);
} else if (RhinoOperation.GETWITHTHIS.equals(op)) {
tt = mType.insertParameterTypes(1, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "getPropFunctionAndThis", tt);
mh = MethodHandles.insertArguments(mh, 1, name);
mh =
bindStringParameter(
lookup, mType, ScriptRuntime.class, "getPropFunctionAndThis", 1, name);
} else if (StandardOperation.SET.equals(op)) {
tt = mType.insertParameterTypes(1, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "setObjectProp", tt);
mh = MethodHandles.insertArguments(mh, 1, name);
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "setObjectProp", 1, name);
} else if (RhinoOperation.GETELEMENT.equals(op)) {
mh = lookup.findStatic(ScriptRuntime.class, "getObjectElem", mType);
} else if (RhinoOperation.GETINDEX.equals(op)) {
mh = lookup.findStatic(ScriptRuntime.class, "getObjectIndex", mType);
} else if (RhinoOperation.SETELEMENT.equals(op)) {
mh = lookup.findStatic(ScriptRuntime.class, "setObjectElem", mType);
} else if (RhinoOperation.SETINDEX.equals(op)) {
mh = lookup.findStatic(ScriptRuntime.class, "setObjectIndex", mType);
}

if (mh != null) {
Expand All @@ -120,23 +124,24 @@ private GuardedInvocation getNameInvocation(
Operation op,
String name)
throws NoSuchMethodException, IllegalAccessException {
MethodType tt;
MethodHandle mh = null;

// Like above for properties, the name to handle is not on the Java stack,
// but is something that we parsed from the name of the invokedynamic operation.
if (RhinoOperation.BIND.equals(op)) {
tt = mType.insertParameterTypes(2, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "bind", tt);
mh = MethodHandles.insertArguments(mh, 2, name);
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "bind", 2, name);
} else if (StandardOperation.GET.equals(op)) {
tt = mType.insertParameterTypes(2, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "name", tt);
mh = MethodHandles.insertArguments(mh, 2, name);
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "name", 2, name);
} else if (RhinoOperation.GETWITHTHIS.equals(op)) {
tt = mType.insertParameterTypes(0, String.class);
mh = lookup.findStatic(ScriptRuntime.class, "getNameFunctionAndThis", tt);
mh = MethodHandles.insertArguments(mh, 0, name);
mh =
bindStringParameter(
lookup, mType, ScriptRuntime.class, "getNameFunctionAndThis", 0, name);
} else if (StandardOperation.SET.equals(op)) {
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "setName", 4, name);
} else if (RhinoOperation.SETSTRICT.equals(op)) {
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "strictSetName", 4, name);
} else if (RhinoOperation.SETCONST.equals(op)) {
mh = bindStringParameter(lookup, mType, ScriptRuntime.class, "setConst", 3, name);
}

if (mh != null) {
Expand All @@ -145,9 +150,8 @@ private GuardedInvocation getNameInvocation(
throw new UnsupportedOperationException(rootOp.toString());
}

// If the operation is a named operation, then return the name,
// or the empty string if it's not.
static String getName(Operation op) {
/** If the operation is a named operation, then return the name, */
private static String getName(Operation op) {
Object nameObj = NamedOperation.getName(op);
if (nameObj instanceof String) {
return (String) nameObj;
Expand All @@ -157,4 +161,27 @@ static String getName(Operation op) {
return "";
}
}

/**
* Given a class name and a method name, do the following:
*
* <ol>
* <li>insert a String parameter into the method type at "index"
* <li>Lookup a named static method from the specified class using the new type
* <li>Bind a string constant to the method handle at "index"
* <li>Return the bound handle
* </ol>
*/
private static MethodHandle bindStringParameter(
MethodHandles.Lookup lookup,
MethodType mt,
Class<?> cls,
String method,
int index,
String nameParam)
throws NoSuchMethodException, IllegalAccessException {
MethodType actualType = mt.insertParameterTypes(index, String.class);
MethodHandle mh = lookup.findStatic(cls, method, actualType);
return MethodHandles.insertArguments(mh, index, nameParam);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ public enum RhinoOperation implements Operation {
BIND,
GETNOWARN,
GETWITHTHIS,
GETELEMENT,
GETINDEX,
SETSTRICT,
SETCONST,
SETELEMENT,
SETINDEX,
}

0 comments on commit 22df4ed

Please sign in to comment.