Skip to content

Commit

Permalink
#57: Further work to support Intellij plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
jaccomoc committed Nov 7, 2024
1 parent ac5d456 commit ed732dc
Show file tree
Hide file tree
Showing 15 changed files with 121 additions and 65 deletions.
2 changes: 1 addition & 1 deletion src/main/java/io/jactl/DelegatingJactlType.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class DelegatingJactlType extends JactlType {
JactlType delegate;
Expr expr;

DelegatingJactlType(JactlType delegate) {
public DelegatingJactlType(JactlType delegate) {
this.delegate = delegate;
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/jactl/Expr.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ public JactlType patternType() {
this.type;
}

public boolean shouldReportLineNumber() {
// Don't generate line numbers for expressions whose line number is not
// necessarily the start of the expression
return !(this instanceof Expr.Binary || this instanceof Expr.Return || this instanceof MethodCall);
}

////////////////////////////////////////////////////////////////

public static class Binary extends Expr {
public Expr left;
public Token operator;
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/io/jactl/JactlContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,9 @@ private void resumeContinuation(Consumer<Object> completion, Object asyncResult,

private void cleanUp(JactlScriptObject instance) {
RuntimeState.resetState();
if (instance.isCheckpointed()) {
instance.incrementCheckpointId();
deleteCheckpoint(instance.getInstanceId(), instance.checkpointId());
if (instance._$j$isCheckpointed()) {
instance._$j$incrementCheckpointId();
deleteCheckpoint(instance._$j$getInstanceId(), instance._$j$checkpointId());
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/jactl/JactlScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public static Function<Map<String,Object>,Object> createInvoker(Class clazz, Jac
}

private static void cleanUp(JactlScriptObject instance, JactlContext context) {
if (instance.isCheckpointed()) {
context.deleteCheckpoint(instance.getInstanceId(), instance.checkpointId());
if (instance._$j$isCheckpointed()) {
context.deleteCheckpoint(instance._$j$getInstanceId(), instance._$j$checkpointId());
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/main/java/io/jactl/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ private Stmt.FunDecl script() {
}
Token scriptName = start.newIdent(Utils.JACTL_SCRIPT_MAIN);
// Scripts take a single parameter which is a Map of globals
Stmt.VarDecl globalsParam = Utils.createParam(start.newIdent(Utils.JACTL_GLOBALS_NAME), JactlType.MAP);
Stmt.FunDecl funDecl = parseFunDecl(false, scriptName, ANY, Utils.listOf(globalsParam), EOF, true, false, false);
Stmt.VarDecl globalsParam = Utils.createParam(start.newIdent(Utils.JACTL_GLOBALS_NAME).setGenerateLineNumber(false), JactlType.MAP);
Stmt.FunDecl funDecl = parseFunDecl(scriptName, false, scriptName, ANY, Utils.listOf(globalsParam), EOF, true, false, false);
expectOrNull(true, EOF);
Stmt.ClassDecl scriptClass = classes.peek();
scriptClass.scriptMain = funDecl;
Expand Down Expand Up @@ -642,7 +642,7 @@ private Stmt.FunDecl funDecl(boolean inClassDecl) {
mark.drop();
}
matchAny(EOL);
Stmt.FunDecl funDecl = parseFunDecl(true, name, returnType, parameters, RIGHT_BRACE, false, isStatic, isFinal);
Stmt.FunDecl funDecl = parseFunDecl(start, true, name, returnType, parameters, RIGHT_BRACE, false, isStatic, isFinal);
Utils.createVariableForFunction(funDecl);
funDecl.declExpr.varDecl.isField = inClassDecl;
return funDecl;
Expand Down Expand Up @@ -2988,7 +2988,7 @@ private boolean isSimpleIdentOrLiteral(Expr expr) {
}

private Stmt.VarDecl createItParam(Token token) {
return Utils.createParam(token.newIdent(Utils.IT_VAR), ANY, new Expr.Literal(new Token(NULL, token)), true, false);
return Utils.createParam(token.newIdent(Utils.IT_VAR).setGenerateLineNumber(false), ANY, new Expr.Literal(new Token(NULL, token).setGenerateLineNumber(false)), true, false);
}

// Create parameter for class instance initialiser method
Expand Down Expand Up @@ -3072,14 +3072,14 @@ private boolean isAtScriptTopLevel() {
return functionStack().size() == 1 && functionStack().peek().isScriptMain && blockStack().size() == 1;
}

private Stmt.FunDecl parseFunDecl(boolean expectBrace,
private Stmt.FunDecl parseFunDecl(Token start,
boolean expectBrace,
Token name,
JactlType returnType,
List<Stmt.VarDecl> params,
TokenType endToken,
boolean isScriptMain,
boolean isStatic, boolean isFinal) {
Token start = expectBrace ? peekIgnoreEOL() : name;
params.forEach(p -> p.declExpr.isExplicitParam = true);
Expr.FunDecl funDecl = Utils.createFunDecl(start, name, returnType, params, isStatic, false, isFinal);
Stmt.FunDecl funDeclStmt = new Stmt.FunDecl(start, funDecl);
Expand Down
21 changes: 17 additions & 4 deletions src/main/java/io/jactl/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public class Token extends Location {
private Object value; // Value of token - int, long, string, etc if literal
private boolean isKeyword; // Whether token is a keyword or not

// Whether to generate lineNumber for debugging purposes
private boolean generateLineNumber = true;

private JactlError error;

/**
Expand Down Expand Up @@ -79,10 +82,11 @@ public Token(String source, int offset, String line, int lineNum, int col) {
*/
public Token(TokenType type, Token token) {
super(token);
this.type = type;
this.length = token.length;
this.value = token.value;
this.isKeyword = token.isKeyword;
this.type = type;
this.length = token.length;
this.value = token.value;
this.isKeyword = token.isKeyword;
this.generateLineNumber = token.generateLineNumber;
}

/**
Expand All @@ -97,6 +101,15 @@ public Token(JactlError e) {
this.type = EOF;
}

public Token setGenerateLineNumber(boolean generateLineNumber) {
this.generateLineNumber = generateLineNumber;
return this;
}

public boolean isGenerateLineNumber() {
return generateLineNumber;
}

public int getLength() { return length; }

public boolean isError() {
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/io/jactl/compiler/ClassCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,19 @@ public ClassCompiler(String source, JactlContext context, String jactlPkg, Stmt.
constructor.visitVarInsn(ALOAD, 0);
constructor.visitMethodInsn(INVOKESPECIAL, superName, "<init>", "()V", false);

FieldVisitor fieldVisitor = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, Utils.JACTL_FIELDS_METHODS_MAP, MAP.descriptor(), null, null);
FieldVisitor fieldVisitor = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, Utils.JACTL_FIELDS_METHODS_MAP, MAP.descriptor(), null, null);
fieldVisitor.visitEnd();
classInit.visitTypeInsn(NEW, Type.getInternalName(Utils.JACTL_MAP_TYPE));
classInit.visitInsn(DUP);
classInit.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Utils.JACTL_MAP_TYPE), "<init>", "()V", false);
classInit.visitFieldInsn(PUTSTATIC, internalName, Utils.JACTL_FIELDS_METHODS_MAP, "Ljava/util/Map;");
fieldVisitor = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, Utils.JACTL_STATIC_FIELDS_METHODS_MAP, MAP.descriptor(), null, null);
fieldVisitor = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, Utils.JACTL_STATIC_FIELDS_METHODS_MAP, MAP.descriptor(), null, null);
fieldVisitor.visitEnd();
classInit.visitTypeInsn(NEW, Type.getInternalName(Utils.JACTL_MAP_TYPE));
classInit.visitInsn(DUP);
classInit.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Utils.JACTL_MAP_TYPE), "<init>", "()V", false);
classInit.visitFieldInsn(PUTSTATIC, internalName, Utils.JACTL_STATIC_FIELDS_METHODS_MAP, "Ljava/util/Map;");
fieldVisitor = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, Utils.JACTL_PRETTY_NAME_FIELD, Type.getDescriptor(String.class), null, null);
fieldVisitor = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, Utils.JACTL_PRETTY_NAME_FIELD, Type.getDescriptor(String.class), null, null);
fieldVisitor.visitEnd();
classInit.visitLdcInsn(classDescriptor.getPrettyName());
classInit.visitFieldInsn(PUTSTATIC, internalName, Utils.JACTL_PRETTY_NAME_FIELD, Type.getDescriptor(String.class));
Expand Down Expand Up @@ -164,7 +164,7 @@ public ClassCompiler(String source, JactlContext context, String jactlPkg, Stmt.
assert c instanceof List || c instanceof Map;
String fieldName = JACTL_PREFIX + "constant_" + classConstantCnt++;
String descriptor = c instanceof List ? LIST.descriptor() : MAP.descriptor();
FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, fieldName, descriptor, null, null);
FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, fieldName, descriptor, null, null);
fv.visitEnd();
loadConstant(c);
classInit.visitFieldInsn(PUTSTATIC, internalName, fieldName, descriptor);
Expand Down Expand Up @@ -268,7 +268,7 @@ protected void addHandleToClass(Expr.FunDecl funDecl) {
Consumer<Expr.FunDecl> createContinuationHandle = (decl) -> {
String continuationMethod = Utils.continuationMethod(decl.functionDescriptor.implementingMethod);
String handleName = Utils.continuationHandle(decl.functionDescriptor.implementingMethod);
FieldVisitor handleVar = cv.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, handleName, Type.getDescriptor(JactlMethodHandle.class), null, null);
FieldVisitor handleVar = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, handleName, Type.getDescriptor(JactlMethodHandle.class), null, null);
handleVar.visitEnd();

classInit.visitVarInsn(ALOAD, 0);
Expand Down Expand Up @@ -386,14 +386,14 @@ void compileMethod(Expr.FunDecl method) {
// so that we can pass the method by value (by passing the method handle).
String wrapperMethodName = method.wrapper.functionDescriptor.implementingMethod;
String staticHandleName = Utils.staticHandleName(wrapperMethodName);
FieldVisitor handleVar = cv.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, staticHandleName, Type.getDescriptor(JactlMethodHandle.class), null, null);
FieldVisitor handleVar = cv.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, staticHandleName, Type.getDescriptor(JactlMethodHandle.class), null, null);
handleVar.visitEnd();
if (!method.isStatic() && method.isClosure()) {
// For non-static closures we also create a non-static method handle for the wrapper method
// that will be bound to the instance. For non-static functions we bind the handle when we
// compile the declaration and store it in the variable.
String instanceHandleName = Utils.handleName(wrapperMethodName);
handleVar = cv.visitField(ACC_PUBLIC, instanceHandleName, Type.getDescriptor(JactlMethodHandle.class), null, null);
handleVar = cv.visitField(ACC_PUBLIC | ACC_SYNTHETIC, instanceHandleName, Type.getDescriptor(JactlMethodHandle.class), null, null);
handleVar.visitEnd();

// Add code to constructor to initialise this handle
Expand Down
20 changes: 12 additions & 8 deletions src/main/java/io/jactl/compiler/MethodCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,13 @@ Void compile(Expr expr) {
return null;
}
try {
if (expr.location != null && expr.location.isGenerateLineNumber() && expr.location.getLineNum() != currentLineNum && expr.shouldReportLineNumber()) {
currentLineNum = expr.location.getLineNum();
Label label = new Label();
mv.visitLabel(label);
mv.visitLineNumber(currentLineNum, label);
}

if (expr.isConst) {
if (expr.isResultUsed) {
loadConst(expr.constValue);
Expand All @@ -480,12 +487,6 @@ Void compile(Expr expr) {
return null;
}

if (expr.location != null && expr.location.getLineNum() != currentLineNum) {
currentLineNum = expr.location.getLineNum();
Label label = new Label();
mv.visitLabel(label);
mv.visitLineNumber(currentLineNum, label);
}
try {
if (classCompiler.annotate()) {
_loadConst(Utils.repeat(" ", indent++) + "==> " + expr.getClass().getName() + "<" + expr.hashCode() + ">");
Expand Down Expand Up @@ -4441,8 +4442,11 @@ void undefineVar(Expr.VarDecl varDecl, Label endBlock) {
return;
}
JactlType type = varDecl.isHeapLocal ? ANY : varDecl.type;
mv.visitLocalVariable(varDecl.name.getStringValue(), type.descriptor(), null,
varDecl.declLabel, endBlock, varDecl.slot);
if (!varDecl.name.getStringValue().startsWith(Utils.JACTL_PREFIX)) {
// Add debug info for variables (but not internal ones)
mv.visitLocalVariable(varDecl.name.getStringValue(), type.descriptor(), null,
varDecl.declLabel, endBlock, varDecl.slot);
}
stack.freeSlot(varDecl.slot);
varDecl.slot = -1;
}
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/io/jactl/resolver/Resolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -2821,7 +2821,9 @@ private Stmt doExplicitReturn(Stmt stmt, JactlType returnType) {
* <p>The wrapper function will also take care of named argument passing.</p>
*/
private Expr.FunDecl createVarArgWrapper(Expr.FunDecl funDecl) {
Token startToken = funDecl.startToken;
// Copy token so we can set flag that means we won't generate line numbers during code generation
Token startToken = new Token(funDecl.startToken.getType(), funDecl.startToken);
startToken.setGenerateLineNumber(false);

//-----------------------------------
// Some helper lambdas...
Expand Down Expand Up @@ -2958,6 +2960,7 @@ private Expr.FunDecl createVarArgWrapper(Expr.FunDecl funDecl) {
// so no need to recheck each time.)
Expr initialiser;
if (param.initialiser == null) {
param.location.setGenerateLineNumber(false);
Expr getOrThrow = new Expr.InvokeUtility(startToken, RuntimeUtils.class, RuntimeUtils.REMOVE_OR_THROW,
Utils.listOf(Map.class, String.class, boolean.class, String.class, int.class),
Utils.listOf(mapCopyIdent, paramNameIdent,
Expand Down Expand Up @@ -3023,7 +3026,7 @@ private Expr.FunDecl createVarArgWrapper(Expr.FunDecl funDecl) {
// Now invoke the real function (unless we are the init method). For init method we already initialise
// the fields in the varargs wrapper, so we don't need to invoke the non-wrapper version of the function.
Stream<Expr> args = funDecl.parameters.stream()
.map(p -> new Expr.LoadParamValue(p.declExpr.name, p.declExpr));
.map(p -> new Expr.LoadParamValue(startToken.newIdent(p.declExpr.name.getStringValue()), p.declExpr));
if (funDecl.functionDescriptor.needsLocation) {
args = Stream.concat(Stream.of(sourceIdent, offsetIdent), args);
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/jactl/runtime/CheckpointTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public CheckpointTask(String source, int offset) {

@Override
public void execute(JactlContext context, JactlScriptObject instance, Object data, Consumer<Object> resumer) {
instance.incrementCheckpointId();
instance._$j$incrementCheckpointId();
continuation.scriptInstance = instance;
byte[] buf = Checkpointer.checkpoint(continuation, getRuntimeState(), source, offset);
context.saveCheckpoint(instance.getInstanceId(), instance.checkpointId(), buf, source, offset, continuation.localObjects[0], resumer);
context.saveCheckpoint(instance._$j$getInstanceId(), instance._$j$checkpointId(), buf, source, offset, continuation.localObjects[0], resumer);
}
}
33 changes: 27 additions & 6 deletions src/main/java/io/jactl/runtime/ClassDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,37 @@ public ClassDescriptor(String name, boolean isInterface, String javaPackage, Cla
this.pkg = pkgName == null ? "" : pkgName;
this.prettyName = namePath;
int idx = namePath.indexOf(Utils.JACTL_SCRIPT_PREFIX);
// Strip off script name if we are an embedded class
if (idx != -1) {
int dollarIdx = namePath.indexOf('$',idx + Utils.JACTL_SCRIPT_PREFIX.length());
if (idx != -1 ) {
int dollarIdx = namePath.indexOf('$', Utils.JACTL_SCRIPT_PREFIX.length());
if (dollarIdx != -1) {
this.prettyName = namePath.substring(0, idx) + namePath.substring(dollarIdx + 1);
this.prettyName = prettyName.replaceAll("\\$",".");
idx = dollarIdx + 1;
}
else {
// No embedded class so leave as ScriptXYZ
this.prettyName = namePath.substring(Utils.JACTL_PREFIX.length());
idx = -1;
}
}
else {
this.prettyName = prettyName.replaceAll("\\$",".");
// If not script prefix then could be Jactl$$ prefix which is used by IntelliJ plugin
idx = namePath.indexOf("Jactl$$");
if (idx != -1) {
int dollarIdx = namePath.indexOf('$', "Jactl$$".length());
if (dollarIdx != -1) {
idx = dollarIdx + 1;
}
else {
this.prettyName = namePath;
idx = -1;
}
}
}
// Strip off script name if we are an embedded class
if (idx != -1) {
this.prettyName = namePath.substring(idx).replace('$','.');
}
else {
this.prettyName = prettyName.replace('$','.');
}
this.prettyName = (pkg.equals("")?"":(pkg + ".")) + prettyName;
this.packagedName = (pkg.equals("")?"":(pkg + ".")) + namePath;
Expand Down
30 changes: 15 additions & 15 deletions src/main/java/io/jactl/runtime/JactlScriptObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,33 @@
import java.util.UUID;

public class JactlScriptObject implements Checkpointable {
private UUID instanceId = RuntimeUtils.randomUUID();
private int checkpointId = 0;
private UUID _$j$instanceId = RuntimeUtils.randomUUID();
private int _$j$checkpointId = 0;

public UUID getInstanceId() {
return instanceId;
public UUID _$j$getInstanceId() {
return _$j$instanceId;
}

public boolean isCheckpointed() {
return checkpointId > 0;
public boolean _$j$isCheckpointed() {
return _$j$checkpointId > 0;
}

public int checkpointId() {
return checkpointId;
public int _$j$checkpointId() {
return _$j$checkpointId;
}

public void incrementCheckpointId() {
checkpointId++;
public void _$j$incrementCheckpointId() {
_$j$checkpointId++;
}

@Override public void _$j$checkpoint(Checkpointer checkpointer) {
checkpointer.writeLong(instanceId.getMostSignificantBits());
checkpointer.writeLong(instanceId.getLeastSignificantBits());
checkpointer.writeCint(checkpointId);
checkpointer.writeLong(_$j$instanceId.getMostSignificantBits());
checkpointer.writeLong(_$j$instanceId.getLeastSignificantBits());
checkpointer.writeCint(_$j$checkpointId);
}

@Override public void _$j$restore(Restorer restorer) {
instanceId = new UUID(restorer.readLong(), restorer.readLong());
checkpointId = restorer.readCint();
_$j$instanceId = new UUID(restorer.readLong(), restorer.readLong());
_$j$checkpointId = restorer.readCint();
}
}
8 changes: 8 additions & 0 deletions src/main/tools/Expr.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ public JactlType patternType() {
this.type;
}

public boolean shouldReportLineNumber() {
// Don't generate line numbers for expressions whose line number is not
// necessarily the start of the expression
return !(this instanceof Expr.Binary || this instanceof Expr.Return || this instanceof MethodCall);
}

////////////////////////////////////////////////////////////////

class Binary extends Expr {
Expr left;
Token operator;
Expand Down
Loading

0 comments on commit ed732dc

Please sign in to comment.