Skip to content

Commit f5e16b2

Browse files
committed
[GR-65035] Implement JSObject field accesses using JSStubMethods
PullRequest: graal/20829
2 parents 23125d9 + ae89a8a commit f5e16b2

File tree

14 files changed

+397
-375
lines changed

14 files changed

+397
-375
lines changed

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/JSObjectAccessFeature.java

Lines changed: 0 additions & 117 deletions
This file was deleted.

web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/codegen/JSBodyFeature.java

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,29 @@
3838

3939
import com.oracle.graal.pointsto.BigBang;
4040
import com.oracle.graal.pointsto.meta.AnalysisField;
41+
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
4142
import com.oracle.graal.pointsto.meta.AnalysisMethod;
4243
import com.oracle.graal.pointsto.meta.AnalysisType;
4344
import com.oracle.svm.core.ParsingReason;
4445
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4546
import com.oracle.svm.core.feature.InternalFeature;
47+
import com.oracle.svm.core.nodes.SubstrateMethodCallTargetNode;
4648
import com.oracle.svm.hosted.FeatureImpl;
4749
import com.oracle.svm.hosted.ImageClassLoader;
48-
import com.oracle.svm.hosted.webimage.JSObjectAccessFeature;
4950
import com.oracle.svm.hosted.webimage.codegen.node.InterceptJSInvokeNode;
5051
import com.oracle.svm.hosted.webimage.codegen.oop.ClassWithMirrorLowerer;
52+
import com.oracle.svm.hosted.webimage.js.JSObjectAccessMethodSupport;
5153
import com.oracle.svm.hosted.webimage.util.ReflectUtil;
5254
import com.oracle.svm.util.ReflectionUtil;
5355
import com.oracle.svm.webimage.api.Nothing;
5456
import com.oracle.svm.webimage.platform.WebImageJSPlatform;
5557

56-
import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
57-
import jdk.graal.compiler.core.common.type.Stamp;
5858
import jdk.graal.compiler.core.common.type.StampFactory;
59-
import jdk.graal.compiler.nodes.ConstantNode;
59+
import jdk.graal.compiler.core.common.type.StampPair;
60+
import jdk.graal.compiler.nodes.CallTargetNode;
6061
import jdk.graal.compiler.nodes.Invoke;
62+
import jdk.graal.compiler.nodes.InvokeWithExceptionNode;
6163
import jdk.graal.compiler.nodes.ValueNode;
62-
import jdk.graal.compiler.nodes.extended.ForeignCallNode;
6364
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
6465
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext;
6566
import jdk.graal.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
@@ -124,7 +125,7 @@ private boolean canBeJavaScriptCall(AnalysisMethod method) {
124125

125126
@Override
126127
public boolean handleLoadField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field) {
127-
if (ClassWithMirrorLowerer.isJSObjectSubtype(((AnalysisType) field.getDeclaringClass()).getJavaClass())) {
128+
if (ClassWithMirrorLowerer.isFieldRepresentedInJavaScript(field)) {
128129
genJSObjectFieldAccess(b, object, field, null);
129130
return true;
130131
}
@@ -133,44 +134,55 @@ public boolean handleLoadField(GraphBuilderContext b, ValueNode object, Resolved
133134

134135
@Override
135136
public boolean handleStoreField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field, ValueNode value) {
136-
if (ClassWithMirrorLowerer.isJSObjectSubtype(((AnalysisType) field.getDeclaringClass()).getJavaClass())) {
137+
if (ClassWithMirrorLowerer.isFieldRepresentedInJavaScript(field)) {
137138
genJSObjectFieldAccess(b, object, field, value);
138139
return true;
139140
}
140141
return false;
141142
}
142143

143-
private void genJSObjectFieldAccess(GraphBuilderContext b, ValueNode object, ResolvedJavaField field, ValueNode valueForStore) {
144-
ResolvedJavaType fieldType = field.getType().resolve(null);
145-
JavaKind fieldKind = fieldType.getJavaKind();
146-
ConstantNode fieldNameNode = ConstantNode.forConstant(b.getConstantReflection().forString(field.getName()), b.getMetaAccess(), b.getGraph());
144+
/**
145+
* Replaces an access to {@link JSObject} fields with a call to an
146+
* {@link com.oracle.svm.hosted.webimage.js.JSObjectAccessMethod accessor method} that
147+
* performs the access on the underlying JavaScript object.
148+
*
149+
* @param valueForStore If {@code null} is this a load. Otherwise, the value to be
150+
* written into the field.
151+
* @see JSObjectAccessMethodSupport
152+
*/
153+
private static void genJSObjectFieldAccess(GraphBuilderContext b, ValueNode object, ResolvedJavaField field, ValueNode valueForStore) {
154+
AnalysisMetaAccess metaAccess = (AnalysisMetaAccess) b.getMetaAccess();
155+
AnalysisField analysisField = (AnalysisField) field;
147156

148157
boolean isLoad = valueForStore == null;
149158

150-
ForeignCallDescriptor bridgeMethod;
159+
AnalysisMethod accessMethod;
151160
ValueNode[] arguments;
152161
if (isLoad) {
153-
// This is a load access.
154-
bridgeMethod = JSObjectAccessFeature.GETTERS.get(fieldKind);
155-
arguments = new ValueNode[]{object, fieldNameNode};
162+
accessMethod = JSObjectAccessMethodSupport.singleton().lookupLoadMethod(metaAccess, analysisField);
163+
arguments = new ValueNode[]{object};
156164
} else {
157-
// This is a store access.
158-
bridgeMethod = JSObjectAccessFeature.SETTERS.get(fieldKind);
159-
arguments = new ValueNode[]{object, fieldNameNode, valueForStore};
165+
accessMethod = JSObjectAccessMethodSupport.singleton().lookupStoreMethod(metaAccess, analysisField);
166+
arguments = new ValueNode[]{object, valueForStore};
160167
}
161-
ValueNode access = new ForeignCallNode(bridgeMethod, arguments);
168+
169+
JavaKind returnKind = accessMethod.getSignature().getReturnType().getJavaKind();
170+
StampPair returnStamp = StampPair.createSingle(StampFactory.forKind(returnKind));
171+
172+
SubstrateMethodCallTargetNode callTarget = new SubstrateMethodCallTargetNode(CallTargetNode.InvokeKind.Static, accessMethod, arguments, returnStamp);
173+
/*
174+
* Just use a null exception edge. The GraphBuilderContext takes care of wiring it
175+
* up correctly. The exception edge is needed because the access may produce an
176+
* exception during conversions, especially loads, which can cause a
177+
* ClassCastException if JavaScript code stored a value with the wrong type in the
178+
* field.
179+
*/
180+
InvokeWithExceptionNode invoke = new InvokeWithExceptionNode(callTarget, null, b.bci());
181+
162182
if (isLoad) {
163-
b.addPush(fieldKind, access);
164-
// A checkcast is necessary to guard against invalid assignments in JavaScript
165-
// code.
166-
if (fieldKind.isObject()) {
167-
b.pop(fieldKind);
168-
Stamp classStamp = StampFactory.forDeclaredType(null, b.getMetaAccess().lookupJavaType(Class.class), true).getTrustedStamp();
169-
ConstantNode classConstant = b.add(new ConstantNode(b.getConstantReflection().asObjectHub(fieldType.resolve(null)), classStamp));
170-
b.genCheckcastDynamic(access, classConstant);
171-
}
183+
b.addPush(returnKind, invoke);
172184
} else {
173-
b.add(access);
185+
b.add(invoke);
174186
}
175187
}
176188
});

0 commit comments

Comments
 (0)