Skip to content

Commit

Permalink
Copy the contents of the part 12 module into the new part 13 module.
Browse files Browse the repository at this point in the history
  • Loading branch information
skinny85 committed Sep 30, 2023
1 parent 32496ba commit fedfd68
Show file tree
Hide file tree
Showing 88 changed files with 5,894 additions and 1 deletion.
16 changes: 16 additions & 0 deletions part-13/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id 'antlr'
id 'me.champeau.jmh' version '0.6.6'
}

dependencies {
antlr "org.antlr:antlr4:$antlr_version"
implementation "org.graalvm.js:js:$graal_version"
implementation "org.graalvm.tools:profiler:$graal_version"
implementation "org.apache.commons:commons-text:1.10.0"
}

// required to allow GraalVM to discover our EasyScript language class
test {
jvmArgs '-Dgraalvm.locatorDisabled=true'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.endoflineblog.truffle.part_13;

import org.openjdk.jmh.annotations.Benchmark;

/**
* A simple benchmark for calling an instance method of a user-defined class.
*/
public class InstanceMethodBenchmark extends TruffleBenchmark {
private static final int INPUT = 1_000_000;

private static final String ADDER_CLASS = "" +
"class Adder { " +
" add(a, b) { " +
" return a + b; " +
" } " +
"}";

@Override
public void setup() {
super.setup();

this.truffleContext.eval("ezs", ADDER_CLASS);
this.truffleContext.eval("ezs", COUNT_METHOD_PROP_ALLOC_INSIDE_FOR);
this.truffleContext.eval("ezs", COUNT_METHOD_PROP_ALLOC_OUTSIDE_FOR);

this.truffleContext.eval("js", ADDER_CLASS);
this.truffleContext.eval("js", COUNT_METHOD_PROP_ALLOC_INSIDE_FOR);
this.truffleContext.eval("js", COUNT_METHOD_PROP_ALLOC_OUTSIDE_FOR);
}

private static final String COUNT_METHOD_PROP_ALLOC_INSIDE_FOR = "" +
"function countMethodPropAllocInsideFor(n) { " +
" var ret = 0; " +
" for (let i = 0; i < n; i = i + 1) { " +
" ret = new Adder().add(ret, 1); " +
" } " +
" return ret; " +
"}";

@Benchmark
public int count_method_prop_alloc_inside_for_ezs() {
return this.truffleContext.eval("ezs", "countMethodPropAllocInsideFor(" + INPUT + ");").asInt();
}

@Benchmark
public int count_method_prop_alloc_inside_for_js() {
return this.truffleContext.eval("js", "countMethodPropAllocInsideFor(" + INPUT + ");").asInt();
}

private static final String COUNT_METHOD_PROP_ALLOC_OUTSIDE_FOR = "" +
"function countMethodPropAllocOutsideFor(n) { " +
" var ret = 0; " +
" const adder = new Adder(); " +
" for (let i = 0; i < n; i = i + 1) { " +
" ret = adder.add(ret, 1); " +
" } " +
" return ret; " +
"}";

@Benchmark
public int count_method_prop_alloc_outside_for_ezs() {
return this.truffleContext.eval("ezs", "countMethodPropAllocOutsideFor(" + INPUT + ");").asInt();
}

@Benchmark
public int count_method_prop_alloc_outside_for_js() {
return this.truffleContext.eval("js", "countMethodPropAllocOutsideFor(" + INPUT + ");").asInt();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.endoflineblog.truffle.part_13;

import org.graalvm.polyglot.Context;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;

import java.util.concurrent.TimeUnit;

/**
* The common superclass of all JMH benchmarks.
* Identical to the class with the same name from part 11.
*/
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(value = 1, jvmArgsAppend = {
"-Dgraalvm.locatorDisabled=true",
"--add-exports",
"org.graalvm.truffle/com.oracle.truffle.api.staticobject=ALL-UNNAMED"
})
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
public abstract class TruffleBenchmark {
protected Context truffleContext;

@Setup
public void setup() {
this.truffleContext = Context.create();
}

@TearDown
public void tearDown() {
this.truffleContext.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
grammar EasyScript ;

@header{
package com.endoflineblog.truffle.part_13.parsing.antlr;
}

start : stmt+ EOF ;

stmt : kind=('var' | 'let' | 'const') binding (',' binding)* ';'? #VarDeclStmt
| expr1 ';'? #ExprStmt
| 'function' subroutine_decl ';'? #FuncDeclStmt
| 'return' expr1? ';'? #ReturnStmt
| '{' stmt* '}' ';'? #BlockStmt
| 'if' '(' cond=expr1 ')' then_stmt=stmt ('else' else_stmt=stmt)? #IfStmt
| 'while' '(' cond=expr1 ')' body=stmt #WhileStmt
| 'do' '{' stmt* '}' 'while' '(' cond=expr1 ')' ';'? #DoWhileStmt
| 'for' '(' init=stmt? ';' cond=expr1? ';' updt=expr1? ')' body=stmt #ForStmt
| 'break' ';'? #BreakStmt
| 'continue' ';'? #ContinueStmt
| 'class' ID '{' class_member* '}' ';'? #ClassDeclStmt
;
binding : ID ('=' expr1)? ;
class_member : subroutine_decl ;
subroutine_decl : name=ID '(' args=func_args ')' '{' stmt* '}' ;
func_args : (ID (',' ID)* )? ;

expr1 : ID '=' expr1 #AssignmentExpr1
| arr=expr5 '[' index=expr1 ']' '=' rvalue=expr1 #ArrayIndexWriteExpr1
| expr2 #PrecedenceTwoExpr1
;
expr2 : left=expr2 c=('===' | '!==') right=expr3 #EqNotEqExpr2
| expr3 #PrecedenceThreeExpr2
;
expr3 : left=expr3 c=('<' | '<=' | '>' | '>=') right=expr4 #ComparisonExpr3
| expr4 #PrecedenceFourExpr3
;
expr4 : left=expr4 o=('+' | '-') right=expr5 #AddSubtractExpr4
| '-' expr5 #UnaryMinusExpr4
| expr5 #PrecedenceFiveExpr4
;
expr5 : expr5 '.' ID #PropertyReadExpr5
| arr=expr5 '[' index=expr1 ']' #ArrayIndexReadExpr5
| expr5 '(' (expr1 (',' expr1)*)? ')' #CallExpr5
| expr6 #PrecedenceSixExpr5
;
expr6 : literal #LiteralExpr6
| ID #ReferenceExpr6
| '[' (expr1 (',' expr1)*)? ']' #ArrayLiteralExpr6
| 'new' constr=expr6 ('('(expr1 (',' expr1)*)?')')? #NewExpr6
| '(' expr1 ')' #PrecedenceOneExpr6
;

literal : INT | DOUBLE | 'undefined' | bool_literal | string_literal ;
bool_literal : 'true' | 'false' ;

fragment DIGIT : [0-9] ;
INT : DIGIT+ ;
DOUBLE : DIGIT+ '.' DIGIT+ ;

fragment LETTER : [a-zA-Z$_] ;
ID : LETTER (LETTER | DIGIT)* ;

string_literal: SINGLE_QUOTE_STRING | DOUBLE_QUOTE_STRING ;
// see https://stackoverflow.com/questions/24557953/handling-string-literals-which-end-in-an-escaped-quote-in-antlr4
// for details
SINGLE_QUOTE_STRING : '\'' (~[\\'\r\n] | '\\' ~[\r\n])* '\'' ;
DOUBLE_QUOTE_STRING : '"' (~[\\"\r\n] | '\\' ~[\r\n])* '"' ;
// skip all whitespace
WS : (' ' | '\r' | '\t' | '\n' | '\f')+ -> skip ;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.endoflineblog.truffle.part_13;

import com.endoflineblog.truffle.part_13.runtime.GlobalScopeObject;
import com.endoflineblog.truffle.part_13.runtime.StringPrototype;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.Shape;

/**
* The class of the context for the
* {@link EasyScriptTruffleLanguage TruffleLanguage implementaton in this part of the series}.
* Identical to the class with the same name from part 11.
*/
public final class EasyScriptLanguageContext {
private static final TruffleLanguage.ContextReference<EasyScriptLanguageContext> REF =
TruffleLanguage.ContextReference.create(EasyScriptTruffleLanguage.class);

/** Retrieve the current language context for the given {@link Node}. */
public static EasyScriptLanguageContext get(Node node) {
return REF.get(node);
}

public final DynamicObject globalScopeObject;

/**
* The object containing the {@code CallTarget}s
* for the built-in methods of strings.
*/
public final StringPrototype stringPrototype;

public EasyScriptLanguageContext(Shape globalScopeShape, StringPrototype stringPrototype) {
this.globalScopeObject = new GlobalScopeObject(globalScopeShape);
this.stringPrototype = stringPrototype;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.endoflineblog.truffle.part_13;

import com.endoflineblog.truffle.part_13.nodes.exprs.functions.ReadFunctionArgExprNode;
import com.endoflineblog.truffle.part_13.nodes.exprs.functions.built_in.AbsFunctionBodyExprNodeFactory;
import com.endoflineblog.truffle.part_13.nodes.exprs.functions.built_in.BuiltInFunctionBodyExprNode;
import com.endoflineblog.truffle.part_13.nodes.exprs.functions.built_in.PowFunctionBodyExprNodeFactory;
import com.endoflineblog.truffle.part_13.nodes.exprs.functions.built_in.methods.CharAtMethodBodyExprNodeFactory;
import com.endoflineblog.truffle.part_13.nodes.root.BuiltInFuncRootNode;
import com.endoflineblog.truffle.part_13.nodes.root.StmtBlockRootNode;
import com.endoflineblog.truffle.part_13.parsing.EasyScriptTruffleParser;
import com.endoflineblog.truffle.part_13.parsing.ParsingResult;
import com.endoflineblog.truffle.part_13.runtime.ArrayObject;
import com.endoflineblog.truffle.part_13.runtime.FunctionObject;
import com.endoflineblog.truffle.part_13.runtime.MathObject;
import com.endoflineblog.truffle.part_13.runtime.StringPrototype;
import com.endoflineblog.truffle.part_13.runtime.ClassPrototypeObject;
import com.endoflineblog.truffle.part_13.runtime.GlobalScopeObject;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.object.Shape;

import java.util.stream.IntStream;

/**
* The {@link TruffleLanguage} implementation for this part of the article series.
* Very similar to the class with the same name from part 11,
* the only difference is that we rename the field {@code globalScopeShape}
* to {@code rootShape}, as it's now used as the {@link Shape}
* of the {@link ClassPrototypeObject}
* {@link com.oracle.truffle.api.object.DynamicObject},
* in addition to the {@link GlobalScopeObject},
* and we also pass it to the
* {@link EasyScriptTruffleParser#parse main parsing method}.
*/
@TruffleLanguage.Registration(id = "ezs", name = "EasyScript")
public final class EasyScriptTruffleLanguage extends TruffleLanguage<EasyScriptLanguageContext> {
private static final LanguageReference<EasyScriptTruffleLanguage> REF =
LanguageReference.create(EasyScriptTruffleLanguage.class);

/** Retrieve the current language instance for the given {@link Node}. */
public static EasyScriptTruffleLanguage get(Node node) {
return REF.get(node);
}

/** The root {@link Shape} for {@link ArrayObject} */
private final Shape arrayShape = Shape.newBuilder().layout(ArrayObject.class).build();

/**
* The root {@link Shape} for {@link GlobalScopeObject}
* and {@link ClassPrototypeObject}.
*/
private final Shape rootShape = Shape.newBuilder().build();

@Override
protected CallTarget parse(ParsingRequest request) throws Exception {
ParsingResult parsingResult = EasyScriptTruffleParser.parse(
request.getSource().getReader(), this.rootShape, this.arrayShape);
var programRootNode = new StmtBlockRootNode(this, parsingResult.topLevelFrameDescriptor,
parsingResult.programStmtBlock);
return programRootNode.getCallTarget();
}

@Override
protected EasyScriptLanguageContext createContext(Env env) {
var context = new EasyScriptLanguageContext(this.rootShape, this.createStringPrototype());
var globalScopeObject = context.globalScopeObject;

var objectLibrary = DynamicObjectLibrary.getUncached();
// the 1 flag indicates Math is a constant, and cannot be reassigned
objectLibrary.putConstant(globalScopeObject, "Math", MathObject.create(this,
this.defineBuiltInFunction(AbsFunctionBodyExprNodeFactory.getInstance()),
this.defineBuiltInFunction(PowFunctionBodyExprNodeFactory.getInstance())), 1);

return context;
}

@Override
protected Object getScope(EasyScriptLanguageContext context) {
return context.globalScopeObject;
}

private StringPrototype createStringPrototype() {
return new StringPrototype(
this.createCallTarget(CharAtMethodBodyExprNodeFactory.getInstance()));
}

private FunctionObject defineBuiltInFunction(NodeFactory<? extends BuiltInFunctionBodyExprNode> nodeFactory) {
return new FunctionObject(this.createCallTarget(nodeFactory),
nodeFactory.getExecutionSignature().size());
}

private CallTarget createCallTarget(NodeFactory<? extends BuiltInFunctionBodyExprNode> nodeFactory) {
int argumentCount = nodeFactory.getExecutionSignature().size();
ReadFunctionArgExprNode[] functionArguments = IntStream.range(0, argumentCount)
.mapToObj(i -> new ReadFunctionArgExprNode(i))
.toArray(ReadFunctionArgExprNode[]::new);
var rootNode = new BuiltInFuncRootNode(this,
nodeFactory.createNode((Object) functionArguments));
return rootNode.getCallTarget();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.endoflineblog.truffle.part_13;

import com.oracle.truffle.api.dsl.ImplicitCast;
import com.oracle.truffle.api.dsl.TypeSystem;

/**
* The {@link TypeSystem} for EasyScript.
* Identical to the class with the same name from part 11.
*/
@TypeSystem({
boolean.class,
int.class,
double.class,
})
public abstract class EasyScriptTypeSystem {
@ImplicitCast
public static double castIntToDouble(int value) {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.endoflineblog.truffle.part_13.common;

import com.endoflineblog.truffle.part_13.exceptions.EasyScriptException;

/**
* An enum that represents the different kinds of variable declarations in JavaScript.
* Identical to the enum with the same name from part 11.
*/
public enum DeclarationKind {
/** This represents the 'var' declaration kind. */
VAR,

/** This represents the 'let' declaration kind. */
LET,

/** This represents the 'const' declaration kind. */
CONST;

public static DeclarationKind fromToken(String token) {
switch (token) {
case "var": return DeclarationKind.VAR;
case "let": return DeclarationKind.LET;
case "const": return DeclarationKind.CONST;
default: throw new EasyScriptException("Unrecognized variable kind: '" + token + "'");
}
}
}
Loading

0 comments on commit fedfd68

Please sign in to comment.