From b55707ec6ab29f3190c3dd3f0f6e6ff30222daf5 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Sun, 7 Apr 2024 12:22:15 -0700 Subject: [PATCH] Make 'super' static instead of dynamic --- .../nodes/exprs/objects/SuperExprNode.java | 36 ++++--------------- .../parsing/EasyScriptTruffleParser.java | 25 ++++++++++++- .../truffle/part_13/InheritanceTest.java | 28 +++++++++++++++ 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/part-14/src/main/java/com/endoflineblog/truffle/part_13/nodes/exprs/objects/SuperExprNode.java b/part-14/src/main/java/com/endoflineblog/truffle/part_13/nodes/exprs/objects/SuperExprNode.java index 399acd86..a699b93f 100644 --- a/part-14/src/main/java/com/endoflineblog/truffle/part_13/nodes/exprs/objects/SuperExprNode.java +++ b/part-14/src/main/java/com/endoflineblog/truffle/part_13/nodes/exprs/objects/SuperExprNode.java @@ -1,45 +1,23 @@ package com.endoflineblog.truffle.part_13.nodes.exprs.objects; -import com.endoflineblog.truffle.part_13.exceptions.EasyScriptException; import com.endoflineblog.truffle.part_13.nodes.exprs.EasyScriptExprNode; import com.endoflineblog.truffle.part_13.runtime.ClassPrototypeChainObject; -import com.endoflineblog.truffle.part_13.runtime.ClassPrototypeObject; -import com.endoflineblog.truffle.part_13.runtime.JavaScriptObject; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; /** * The Node implementing the 'super' expression. */ public final class SuperExprNode extends EasyScriptExprNode { - static abstract class ReadParentPrototypeNode extends Node { - abstract Object executeReadParentPrototype(Object object); - - @Specialization - protected Object parentPrototypeOfJavaScriptObject(JavaScriptObject javaScriptObject) { - ClassPrototypeObject classPrototypeObject = javaScriptObject.classPrototypeObject; - if (classPrototypeObject instanceof ClassPrototypeChainObject) { - return ((ClassPrototypeChainObject) classPrototypeObject).superClassPrototype; - } else { - throw new EasyScriptException("Class '" + classPrototypeObject.className + - "' does not have a superclass that can be accessed with 'super'"); - } - } - - @Specialization - protected void parentPrototypeOfNonJavaScriptObject(Object object) { - throw new EasyScriptException("Cannot access prototype with 'super' of: " + object); - } - } + private final ClassPrototypeChainObject classPrototype; @SuppressWarnings("FieldMayBeFinal") @Child - private ThisExprNode thisExprNode = new ThisExprNode(); + private ThisExprNode thisExprNode; - @SuppressWarnings("FieldMayBeFinal") - @Child - private ReadParentPrototypeNode readParentPrototypeNode = SuperExprNodeFactory.ReadParentPrototypeNodeGen.create(); + public SuperExprNode(ClassPrototypeChainObject classPrototype) { + this.classPrototype = classPrototype; + this.thisExprNode = new ThisExprNode(); + } @Override public Object executeGeneric(VirtualFrame frame) { @@ -52,6 +30,6 @@ public Object executeGeneric(VirtualFrame frame) { public Object readParentPrototype(Object thisValue) { // this method is called from the property access Nodes // to find the parent prototype - return this.readParentPrototypeNode.executeReadParentPrototype(thisValue); + return this.classPrototype.superClassPrototype; } } diff --git a/part-14/src/main/java/com/endoflineblog/truffle/part_13/parsing/EasyScriptTruffleParser.java b/part-14/src/main/java/com/endoflineblog/truffle/part_13/parsing/EasyScriptTruffleParser.java index 3761b057..bb3327b7 100644 --- a/part-14/src/main/java/com/endoflineblog/truffle/part_13/parsing/EasyScriptTruffleParser.java +++ b/part-14/src/main/java/com/endoflineblog/truffle/part_13/parsing/EasyScriptTruffleParser.java @@ -150,6 +150,13 @@ private static final class ClassPrototype extends FrameMember { */ private int localVariablesCounter; + /** + * The prototype of the class that we are currently parsing. + * Needed to make {@code super} static, instead of dynamic, + * like {@code this} is. + */ + private ClassPrototypeObject currentClassPrototype; + private EasyScriptTruffleParser(Shape objectShape) { this.objectShape = objectShape; this.state = ParserState.TOP_LEVEL; @@ -158,6 +165,7 @@ private EasyScriptTruffleParser(Shape objectShape) { // we add a global scope, in which we store the class prototypes this.localScopes.push(new HashMap<>()); this.localVariablesCounter = 0; + this.currentClassPrototype = null; } private List parseStmtsList(List stmts) { @@ -353,11 +361,15 @@ private EasyScriptStmtNode parseClassDeclStmt(EasyScriptParser.ClassDeclStmtCont } } this.localScopes.get(0).put(className, new ClassPrototype(classPrototype)); + this.currentClassPrototype = classPrototype; + List classMethods = new ArrayList<>(); for (var classMember : classDeclStmt.class_member()) { classMethods.add(this.parseSubroutineDecl(classMember.subroutine_decl(), new DynamicObjectReferenceExprNode(classPrototype))); } + + this.currentClassPrototype = null; return GlobalVarDeclStmtNodeGen.create( GlobalScopeObjectExprNodeGen.create(), new ClassDeclExprNode(classMethods, classPrototype), @@ -533,7 +545,7 @@ private EasyScriptExprNode parseExpr6(EasyScriptParser.Expr6Context expr6) { } else if (expr6 instanceof EasyScriptParser.ThisExpr6Context) { return new ThisExprNode(); } else if (expr6 instanceof EasyScriptParser.SuperExpr6Context) { - return new SuperExprNode(); + return this.parseSuperExpr((EasyScriptParser.SuperExpr6Context) expr6); } else if (expr6 instanceof EasyScriptParser.ReferenceExpr6Context) { return this.parseReference(((EasyScriptParser.ReferenceExpr6Context) expr6).ID().getText()); } else if (expr6 instanceof EasyScriptParser.ArrayLiteralExpr6Context) { @@ -569,6 +581,17 @@ private EasyScriptExprNode parseLiteralExpr(EasyScriptParser.LiteralExpr6Context return new UndefinedLiteralExprNode(); } + private EasyScriptExprNode parseSuperExpr(EasyScriptParser.SuperExpr6Context superExpr) { + if (this.currentClassPrototype == null) { + throw new EasyScriptException("'super' is only available in class declarations"); + } + if (!(this.currentClassPrototype instanceof ClassPrototypeChainObject)) { + throw new EasyScriptException("Cannot use 'super' in class '" + this.currentClassPrototype.className + + "' without a superclass"); + } + return new SuperExprNode((ClassPrototypeChainObject) this.currentClassPrototype); + } + private EasyScriptExprNode parseReference(String variableId) { FrameMember frameMember = this.findFrameMember(variableId); if (frameMember == null || frameMember instanceof ClassPrototype) { diff --git a/part-14/src/test/java/com/endoflineblog/truffle/part_13/InheritanceTest.java b/part-14/src/test/java/com/endoflineblog/truffle/part_13/InheritanceTest.java index c78414d7..c0a436d5 100644 --- a/part-14/src/test/java/com/endoflineblog/truffle/part_13/InheritanceTest.java +++ b/part-14/src/test/java/com/endoflineblog/truffle/part_13/InheritanceTest.java @@ -66,6 +66,34 @@ void super_reads_property_of_parent_prototype() { assertEquals("Base_Derived", result.asString()); } + @Test + void super_is_static_not_dynamic() { + Value result = this.context.eval("ezs", "" + + "class Base { " + + " m() { " + + " return 'Base'; " + + " } " + + "} " + + "class Middle extends Base { " + + " m() { " + + " return 'Middle'; " + + " } " + + " callSuperM() { " + + " return super.m(); " + + " } " + + "} " + + "class Derived extends Middle { " + + " m() {" + + " return 'Derived'; " + + " } " + + "} " + + "const obj = new Derived(); " + + "obj.callSuperM();" + ); + + assertEquals("Base", result.asString()); + } + @Test void extending_non_existent_class_is_an_error() { try {