-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copy the contents of the part 12 module into the new part 13 module.
- Loading branch information
Showing
88 changed files
with
5,894 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
} |
69 changes: 69 additions & 0 deletions
69
part-13/src/jmh/java/com/endoflineblog/truffle/part_13/InstanceMethodBenchmark.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
part-13/src/jmh/java/com/endoflineblog/truffle/part_13/TruffleBenchmark.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
part-13/src/main/antlr/com/endoflineblog/truffle/part_13/parsing/antlr/EasyScript.g4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ; |
36 changes: 36 additions & 0 deletions
36
part-13/src/main/java/com/endoflineblog/truffle/part_13/EasyScriptLanguageContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
part-13/src/main/java/com/endoflineblog/truffle/part_13/EasyScriptTruffleLanguage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
part-13/src/main/java/com/endoflineblog/truffle/part_13/EasyScriptTypeSystem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
part-13/src/main/java/com/endoflineblog/truffle/part_13/common/DeclarationKind.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 + "'"); | ||
} | ||
} | ||
} |
Oops, something went wrong.