package com.oracle.truffle.trufflenode.node;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.Tag;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.JavaScriptNode;
import com.oracle.truffle.js.nodes.access.JSConstantNode;
import com.oracle.truffle.js.nodes.access.ObjectLiteralNode;
import com.oracle.truffle.js.nodes.access.ObjectLiteralNode.ObjectLiteralMemberNode;
import com.oracle.truffle.js.nodes.access.PropertySetNode;
import com.oracle.truffle.js.nodes.function.JSFunctionExpressionNode;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.Symbol;
import com.oracle.truffle.js.runtime.builtins.JSFunctionData;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.Pair;
import com.oracle.truffle.trufflenode.GraalJSAccess;
import com.oracle.truffle.trufflenode.info.Accessor;
import com.oracle.truffle.trufflenode.info.FunctionTemplate;
import com.oracle.truffle.trufflenode.info.ObjectTemplate;
import com.oracle.truffle.trufflenode.info.Value;
public class ObjectTemplateNode extends JavaScriptBaseNode {
@Children ObjectLiteralNode.ObjectLiteralMemberNode[] members;
private final JSContext context;
ObjectTemplateNode(ObjectLiteralNode.ObjectLiteralMemberNode[] members, JSContext context) {
this.members = members;
this.context = context;
}
@ExplodeLoop
public DynamicObject executeWithObject(VirtualFrame frame, DynamicObject object) {
for (int i = 0; i < members.length; i++) {
members[i].executeVoid(frame, object, context);
}
return object;
}
public static ObjectTemplateNode fromObjectTemplate(ObjectTemplate template, JSContext context, GraalJSAccess graalJSAccess) {
List<ObjectLiteralNode.ObjectLiteralMemberNode> members = new ArrayList<>();
for (Accessor accessor : template.getAccessors()) {
Pair<JSFunctionData, JSFunctionData> pair = accessor.getFunctions(context);
JavaScriptNode getterNode = null;
JavaScriptNode setterNode = null;
if (pair.getFirst() != null) {
getterNode = ObjectLiteralNode.MakeMethodNode.createWithKey(context, JSFunctionExpressionNode.create(pair.getFirst(), null), GraalJSAccess.HOLDER_KEY);
}
if (pair.getSecond() != null) {
setterNode = ObjectLiteralNode.MakeMethodNode.createWithKey(context, JSFunctionExpressionNode.create(pair.getSecond(), null), GraalJSAccess.HOLDER_KEY);
}
members.add(ObjectLiteralNode.newAccessorMember(accessor.getName(), false, accessor.getAttributes(), getterNode, setterNode));
}
for (Value value : template.getValues()) {
JavaScriptNode valueNode;
Object propertyValue = value.getValue();
if (propertyValue instanceof FunctionTemplate) {
FunctionTemplate functionTempl = (FunctionTemplate) propertyValue;
valueNode = JSConstantNode.create(graalJSAccess.functionTemplateGetFunction(context.getRealm(), functionTempl));
} else {
valueNode = JSConstantNode.create(propertyValue);
}
Object name = value.getName();
int attributes = value.getAttributes();
if (propertyValue instanceof Pair) {
Pair<?, ?> pair = (Pair<?, ?>) propertyValue;
JSRealm realm = context.getRealm();
Object getterTemplate = pair.getFirst();
Object setterTemplate = pair.getSecond();
Object getter = (getterTemplate == null) ? Undefined.instance : graalJSAccess.functionTemplateGetFunction(realm, getterTemplate);
Object setter = (setterTemplate == null) ? Undefined.instance : graalJSAccess.functionTemplateGetFunction(realm, setterTemplate);
JavaScriptNode getterNode = JSConstantNode.create(getter);
JavaScriptNode setterNode = JSConstantNode.create(setter);
members.add(ObjectLiteralNode.newAccessorMember(name, false, attributes, getterNode, setterNode));
} else if (name instanceof String || name instanceof Symbol) {
members.add(ObjectLiteralNode.newDataMember(name, false, attributes, valueNode));
} else if (name instanceof HiddenKey) {
if (!template.hasPropertyHandler()) {
members.add(new InternalFieldNode(false, attributes, (HiddenKey) name, propertyValue, context));
}
} else {
members.add(ObjectLiteralNode.newComputedDataMember(JSConstantNode.create(name), false, attributes, valueNode));
}
}
return new ObjectTemplateNode(members.toArray(ObjectLiteralNode.ObjectLiteralMemberNode.EMPTY), context);
}
private static final class InternalFieldNode extends ObjectLiteralNode.ObjectLiteralMemberNode {
@Child PropertySetNode setNode;
private final Object value;
private InternalFieldNode(boolean isStatic, int attributes, HiddenKey key, Object value, JSContext context) {
super(isStatic, attributes);
this.setNode = PropertySetNode.createSetHidden(key, context);
this.value = value;
}
@Override
public void executeVoid(VirtualFrame frame, DynamicObject receiver, DynamicObject homeObject, JSContext context) {
setNode.setValue(receiver, value);
}
@Override
protected ObjectLiteralMemberNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
return new InternalFieldNode(isStatic, attributes, (HiddenKey) setNode.getKey(), value, setNode.getContext());
}
}
}