Java tutorial
/* Redline Smalltalk, Copyright (c) James C. Ladd. All rights reserved. See LICENSE in the root of this distribution. */ package st.redline.compiler; import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.objectweb.asm.*; import st.redline.classloader.SmalltalkClassLoader; import st.redline.classloader.Source; import java.math.BigDecimal; import java.util.*; public class SmalltalkGeneratingVisitor extends SmalltalkBaseVisitor<Void> implements SmalltalkVisitor<Void>, Opcodes { public static final String DEFAULT_IMPORTED_PACKAGE = "st.redline.kernel"; private static final String[] SIGNATURES = { "(Ljava/lang/String;)Lst/redline/core/PrimObject;", "(Lst/redline/core/PrimObject;Ljava/lang/String;)Lst/redline/core/PrimObject;", "(Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Ljava/lang/String;)Lst/redline/core/PrimObject;", "(Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Ljava/lang/String;)Lst/redline/core/PrimObject;", "(Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Ljava/lang/String;)Lst/redline/core/PrimObject;", "(Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Ljava/lang/String;)Lst/redline/core/PrimObject;" }; private static final Map<String, Integer> OPCODES = new HashMap<String, Integer>(); private static final int BYTECODE_VERSION; static { int compareTo18 = new BigDecimal(System.getProperty("java.specification.version")) .compareTo(new BigDecimal("1.8")); if (compareTo18 >= 0) { BYTECODE_VERSION = V1_8; } else { throw new RuntimeException("Java 1.8 or above required."); } } private final Stack<SmalltalkVisitor<Void>> visitors = new Stack<SmalltalkVisitor<Void>>(); private final Source source; private byte[] classBytes = null; public SmalltalkGeneratingVisitor(Source source) { this.source = source; makeClassGeneratorCurrentVisitor(); } private void makeClassGeneratorCurrentVisitor() { pushCurrentVisitor(new ClassGeneratorVisitor()); } public Void visitScript(@NotNull SmalltalkParser.ScriptContext ctx) { currentVisitor().visitScript(ctx); return null; } private void pushCurrentVisitor(SmalltalkVisitor<Void> visitor) { visitors.push(visitor); } private SmalltalkVisitor<Void> currentVisitor() { return visitors.peek(); } private SmalltalkVisitor<Void> popCurrentVisitor() { return visitors.pop(); } private void log(String output) { System.out.println(output); System.out.flush(); } private String className() { return source.className(); } private String sourceFileExtension() { return source.fileExtension(); } private String fullClassName() { return source.fullClassName(); } private String packageName() { return source.packageName(); } private String superclassName() { return "st/redline/core/PrimObject"; } private String contextName() { return "st/redline/core/PrimContext"; } public byte[] generatedClassBytes() { return classBytes; } private int opcodeValue(String opcode) { if (!OPCODES.containsKey(opcode)) throw new IllegalStateException("Unknown OPCODE '" + opcode + "'."); return OPCODES.get(opcode); } public void pop(MethodVisitor mv) { mv.visitInsn(POP); } public void pushBoolean(MethodVisitor mv, boolean value) { mv.visitInsn(value ? ICONST_1 : ICONST_0); } public void pushLiteral(MethodVisitor mv, String literal) { mv.visitLdcInsn(literal); } public void pushDuplicate(MethodVisitor mv) { mv.visitInsn(DUP); } public void pushThis(MethodVisitor mv) { mv.visitVarInsn(ALOAD, 0); } public void pushReceiver(MethodVisitor mv) { mv.visitVarInsn(ALOAD, 1); } public void pushSuper(MethodVisitor mv, int line) { visitLine(mv, line); pushReceiver(mv); } public void pushContext(MethodVisitor mv) { mv.visitVarInsn(ALOAD, 2); } public void pushNull(MethodVisitor mv) { mv.visitInsn(ACONST_NULL); } public void pushTemporary(MethodVisitor mv, int index) { pushContext(mv); pushNumber(mv, index); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "temporaryAt", "(I)Lst/redline/core/PrimObject;", false); } public void pushHomeTemporary(MethodVisitor mv, int index) { pushContext(mv); pushNumber(mv, index); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "homeTemporaryAt", "(I)Lst/redline/core/PrimObject;", false); } public void storeTemporary(MethodVisitor mv, int index) { pushNumber(mv, index); pushContext(mv); mv.visitMethodInsn(INVOKESTATIC, contextName(), "temporaryPutAt", "(Lst/redline/core/PrimObject;IL" + contextName() + ";)V", false); } public void pushInstVar(MethodVisitor mv, String var) { pushContext(mv); pushLiteral(mv, var); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "instVarAt", "(Ljava/lang/String;)Lst/redline/core/PrimObject;", false); } public void storeInstVar(MethodVisitor mv, String identifier) { pushLiteral(mv, identifier); pushContext(mv); mv.visitMethodInsn(INVOKESTATIC, contextName(), "instVarPutAt", "(Lst/redline/core/PrimObject;Ljava/lang/String;L" + contextName() + ";)V", false); } public void storeHomeTemporary(MethodVisitor mv, int index) { pushNumber(mv, index); pushContext(mv); mv.visitMethodInsn(INVOKESTATIC, contextName(), "homeTemporaryPutAt", "(Lst/redline/core/PrimObject;IL" + contextName() + ";)V", false); } public void pushArgument(MethodVisitor mv, int index) { pushContext(mv); pushNumber(mv, index); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "argumentAt", "(I)Lst/redline/core/PrimObject;", false); } public void pushOuterArgument(MethodVisitor mv, int index) { pushContext(mv); pushNumber(mv, index); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "outerArgumentAt", "(I)Lst/redline/core/PrimObject;", false); } public void pushHomeArgument(MethodVisitor mv, int index) { pushContext(mv); pushNumber(mv, index); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "homeArgumentAt", "(I)Lst/redline/core/PrimObject;", false); } public void pushReference(MethodVisitor mv, String name) { pushReceiver(mv); pushLiteral(mv, name); mv.visitMethodInsn(INVOKEVIRTUAL, superclassName(), "reference", "(Ljava/lang/String;)Lst/redline/core/PrimObject;", false); } public void pushNil(MethodVisitor mv) { pushReceiver(mv); mv.visitMethodInsn(INVOKEVIRTUAL, superclassName(), "referenceNil", "()Lst/redline/core/PrimObject;", false); } public void pushTrue(MethodVisitor mv) { pushReceiver(mv); mv.visitMethodInsn(INVOKEVIRTUAL, superclassName(), "referenceTrue", "()Lst/redline/core/PrimObject;", false); } public void pushFalse(MethodVisitor mv) { pushReceiver(mv); mv.visitMethodInsn(INVOKEVIRTUAL, superclassName(), "referenceFalse", "()Lst/redline/core/PrimObject;", false); } public void invokePerform(MethodVisitor mv, String selector, int argumentCount, boolean sendToSuper) { pushLiteral(mv, selector); String methodName = (sendToSuper) ? "superPerform" : "perform"; mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", methodName, SIGNATURES[argumentCount], false); } public void visitLine(MethodVisitor mv, int line) { Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(line, l0); } public static void pushNumber(MethodVisitor mv, int value) { switch (value) { case 0: mv.visitInsn(ICONST_0); break; case 1: mv.visitInsn(ICONST_1); break; case 2: mv.visitInsn(ICONST_2); break; case 3: mv.visitInsn(ICONST_3); break; case 4: mv.visitInsn(ICONST_4); break; case 5: mv.visitInsn(ICONST_5); break; default: if (value > 5 && value < 128) mv.visitIntInsn(BIPUSH, value); else // SIPUSH not supported yet. throw new IllegalStateException("push of integer value " + value + " not yet supported."); } } public void pushNewObject(MethodVisitor mv, String type, String value, int line) { visitLine(mv, line); pushReceiver(mv); pushLiteral(mv, value); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", type, "(Ljava/lang/Object;)Lst/redline/core/PrimObject;", false); } private void pushNewBlock(MethodVisitor mv, String className, String name, String sig, int line, boolean answerBlock, String answerBlockClassName) { pushNewLambda(mv, className, name, sig, line); pushContext(mv); if (!answerBlock) { mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "smalltalkBlock", "(Ljava/lang/Object;Lst/redline/core/PrimContext;)Lst/redline/core/PrimObject;", false); } else { pushLiteral(mv, answerBlockClassName); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "smalltalkBlockAnswer", "(Ljava/lang/Object;Lst/redline/core/PrimContext;Ljava/lang/String;)Lst/redline/core/PrimObject;", false); } } private void pushNewMethod(MethodVisitor mv, String className, String name, String sig, int line) { pushNewLambda(mv, className, name, sig, line); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "smalltalkMethod", "(Ljava/lang/Object;)Lst/redline/core/PrimObject;", false); } private void pushNewLambda(MethodVisitor mv, String className, String name, String sig, int line) { visitLine(mv, line); pushReceiver(mv); mv.visitInvokeDynamicInsn("apply", "()Lst/redline/core/LambdaBlock;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"), new Object[] { Type.getType(sig), new Handle(Opcodes.H_INVOKESTATIC, className, name, sig), Type.getType(sig) }); } // ------------------------------ private class ClassGeneratorVisitor extends SmalltalkBaseVisitor<Void> implements SmalltalkVisitor<Void>, Opcodes { protected final String LAMBDA_BLOCK_SIG = "(Lst/redline/core/PrimObject;Lst/redline/core/PrimObject;Lst/redline/core/PrimContext;)Lst/redline/core/PrimObject;"; protected MethodVisitor mv; private final String SEND_MESSAGES_SIG = "(Lst/redline/core/PrimObject;Lst/redline/core/PrimContext;)Lst/redline/core/PrimObject;"; private final ClassWriter cw; private HashMap<String, ExtendedTerminalNode> temporaries = new HashMap<>(); protected HashMap<String, ExtendedTerminalNode> homeTemporaries; private HashMap<String, ExtendedTerminalNode> arguments = new HashMap<>(); protected HashMap<String, ExtendedTerminalNode> outerArguments; protected HashMap<String, ExtendedTerminalNode> homeArguments; private Stack<KeywordRecord> keywords = new Stack<>(); protected int blockNumber = 0; private boolean referencedJVM = false; private boolean sendToSuper = false; private List<BlockAnswerRecord> tryCatchRecords; private Label tryStartLabel; private Label tryEndLabel; public ClassGeneratorVisitor() { this(new ClassWriter(ClassWriter.COMPUTE_FRAMES)); } public ClassGeneratorVisitor(ClassWriter classWriter) { this.cw = classWriter; } public ClassGeneratorVisitor(ClassWriter classWriter, MethodVisitor methodVisitor) { cw = classWriter; mv = methodVisitor; } public Void visitScript(SmalltalkParser.ScriptContext ctx) { openJavaClass(); createPackageNameMethod(); createImportForMethod(); openSendMessagesMethod(); ctx.sequence().accept(currentVisitor()); closeSendMessagesMethod(); closeJavaClass(); return null; } private void closeSendMessagesMethod() { mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } private void createPackageNameMethod() { mv = cw.visitMethod(ACC_PROTECTED, "packageName", "()Ljava/lang/String;", null, null); mv.visitCode(); mv.visitLdcInsn(packageName()); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private void createImportForMethod() { if (DEFAULT_IMPORTED_PACKAGE.equals(packageName())) return; mv = cw.visitMethod(ACC_PROTECTED, "importFor", "(Ljava/lang/String;)Ljava/lang/String;", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "classLoader", "()Lst/redline/classloader/SmalltalkClassLoader;", false); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "packageName", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/classloader/SmalltalkClassLoader", "importForBy", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); mv.visitInsn(ARETURN); mv.visitMaxs(3, 2); mv.visitEnd(); } private void openSendMessagesMethod() { mv = cw.visitMethod(ACC_PROTECTED, "sendMessages", SEND_MESSAGES_SIG, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 1); } private void openJavaClass() { System.out.println("\nopenJavaClass: " + fullClassName()); cw.visit(BYTECODE_VERSION, ACC_PUBLIC + ACC_SUPER, fullClassName(), null, superclassName(), null); cw.visitSource(className() + sourceFileExtension(), null); cw.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", "java/lang/invoke/MethodHandles", "Lookup", ACC_PUBLIC + ACC_FINAL + ACC_STATIC); makeJavaClassInitializer(); } private void closeJavaClass() { System.out.println("closeJavaClass: " + fullClassName()); cw.visitEnd(); classBytes = cw.toByteArray(); } private void makeJavaClassInitializer() { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitLineNumber(0, l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, superclassName(), "<init>", "()V", false); // import current package mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "packageName", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", "importAll", "(Ljava/lang/String;)V", false); // create a Context mv.visitTypeInsn(NEW, contextName()); mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, contextName(), "<init>", "(Lst/redline/core/PrimObject;)V", false); mv.visitVarInsn(ASTORE, 1); // set the class of this as self. mv.visitVarInsn(ALOAD, 0); // this mv.visitVarInsn(ALOAD, 0); // receiver mv.visitMethodInsn(INVOKEVIRTUAL, fullClassName(), "selfClass", "(Lst/redline/core/PrimObject;)V", false); // call sendMessages with parameters: this & context mv.visitVarInsn(ALOAD, 0); // this mv.visitVarInsn(ALOAD, 0); // receiver mv.visitVarInsn(ALOAD, 1); // context mv.visitMethodInsn(INVOKEVIRTUAL, fullClassName(), "sendMessages", SEND_MESSAGES_SIG, false); mv.visitInsn(POP); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } public Void visitSequence(SmalltalkParser.SequenceContext ctx) { log("visitSequence"); SmalltalkParser.TempsContext temps = ctx.temps(); if (temps != null) temps.accept(currentVisitor()); SmalltalkParser.StatementsContext statements = ctx.statements(); if (statements != null) statements.accept(currentVisitor()); return null; } public Void visitTemps(@NotNull SmalltalkParser.TempsContext ctx) { addToTemporaryVariableMap(ctx.IDENTIFIER()); addTemporariesToContext(); return null; } private void addTemporariesToContext() { visitLine(mv, lineNumberOfFirstTemporary()); pushContext(mv); pushNumber(mv, temporaries.size()); mv.visitMethodInsn(INVOKEVIRTUAL, contextName(), "initTemporaries", "(I)V", false); } private int lineNumberOfFirstTemporary() { return temporaries.entrySet().iterator().next().getValue().getLine(); } private boolean isTemporary(String key) { return temporaries != null && temporaries.containsKey(key); } private boolean isHomeTemporary(String key) { return homeTemporaries != null && homeTemporaries.containsKey(key); } private int indexOfTemporary(String key) { return temporaries.get(key).getIndex(); } private int indexOfHomeTemporary(String key) { return homeTemporaries.get(key).getIndex(); } private void addToTemporaryVariableMap(List<TerminalNode> nodes) { for (TerminalNode node : nodes) addToTemporaryVariableMap(node); } private void addToTemporaryVariableMap(TerminalNode node) { addToTemporaryVariableMap(node.getText(), temporaries.size(), node); } private void addToTemporaryVariableMap(String key, int index, TerminalNode node) { if (temporaries.containsKey(key)) throw new RuntimeException("Temporary '" + key + "' already defined."); temporaries.put(key, new ExtendedTerminalNode(node, index)); } protected int countOf(String input, char ch) { int count = 0; for (int i = 0, l = input.length(); i < l; i++) if (input.charAt(i) == ch) count++; return count; } private boolean haveKeyword() { return !keywords.isEmpty(); } protected void initializeKeyword() { keywords.push(new KeywordRecord()); } protected void addToKeyword(String keyword) { keywords.peek().keyword.append(keyword); } private void addArgumentToKeyword(String firstArgument) { assert keywords.peek().firstArgument.length() == 0; keywords.peek().firstArgument.append(firstArgument); } protected String removeKeyword() { return keywords.pop().keyword.toString(); } private KeywordRecord peekKeyword() { return keywords.peek(); } private boolean isArgument(String key) { return arguments != null && arguments.containsKey(key); } private boolean isOuterArgument(String key) { return outerArguments != null && outerArguments.containsKey(key); } private boolean isHomeArgument(String key) { return homeArguments != null && homeArguments.containsKey(key); } private int indexOfArgument(String key) { return arguments.get(key).getIndex(); } private int indexOfOuterArgument(String key) { return outerArguments.get(key).getIndex(); } private int indexOfHomeArgument(String key) { return homeArguments.get(key).getIndex(); } protected void addArgumentToMap(ExtendedTerminalNode node) { log("addArgumentToMap " + node.getText()); assert !arguments.containsKey(node.getText()); String key = node.getText(); if (key.startsWith(":")) // <- block arguments are prefixed with ':' key = key.substring(1); arguments.put(key, node); } public Void visitStatementExpressions(@NotNull SmalltalkParser.StatementExpressionsContext ctx) { log("visitStatementExpressions"); ctx.expressions().accept(currentVisitor()); return null; } public Void visitStatementExpressionsAnswer( @NotNull SmalltalkParser.StatementExpressionsAnswerContext ctx) { log("visitStatementExpressionsAnswer"); ctx.expressions().accept(currentVisitor()); ctx.answer().accept(currentVisitor()); return null; } public Void visitStatementAnswer(@NotNull SmalltalkParser.StatementAnswerContext ctx) { log("visitStatementAnswer"); SmalltalkParser.AnswerContext answer = ctx.answer(); visitLine(mv, answer.CARROT().getSymbol().getLine()); SmalltalkParser.ExpressionContext expression = answer.expression(); expression.accept(currentVisitor()); mv.visitInsn(ARETURN); return null; } public Void visitAnswer(@NotNull SmalltalkParser.AnswerContext ctx) { log("visitAnswer"); TerminalNode carrot = ctx.CARROT(); visitLine(mv, carrot.getSymbol().getLine()); SmalltalkParser.ExpressionContext expression = ctx.expression(); expression.accept(currentVisitor()); mv.visitInsn(ARETURN); return null; } public Void visitExpression(@NotNull SmalltalkParser.ExpressionContext ctx) { log("\nvisitExpression"); referencedJVM = false; removeJVMGeneratorVisitor(); SmalltalkParser.BinarySendContext binarySend = ctx.binarySend(); if (binarySend != null) return binarySend.accept(currentVisitor()); SmalltalkParser.KeywordSendContext keywordSend = ctx.keywordSend(); if (keywordSend != null) return keywordSend.accept(currentVisitor()); SmalltalkParser.CascadeContext cascade = ctx.cascade(); if (cascade != null) return cascade.accept(currentVisitor()); SmalltalkParser.AssignmentContext assignment = ctx.assignment(); if (assignment != null) return assignment.accept(currentVisitor()); SmalltalkParser.PrimitiveContext primitiveContext = ctx.primitive(); if (primitiveContext != null) return primitiveContext.accept(currentVisitor()); throw new RuntimeException("visitExpression no alternative found."); } private void removeJVMGeneratorVisitor() { if (currentVisitor() instanceof JVMGeneratorVisitor) popCurrentVisitor(); } public Void visitPrimitive(@NotNull SmalltalkParser.PrimitiveContext ctx) { log("visitPrimitive"); throw new RuntimeException("Smalltalk <primitive> should be replaced with JVM primitive: id."); } public Void visitUnarySend(@NotNull SmalltalkParser.UnarySendContext ctx) { log("visitUnarySend"); ctx.operand().accept(currentVisitor()); SmalltalkParser.UnaryTailContext unaryTail = ctx.unaryTail(); if (unaryTail != null) return unaryTail.accept(currentVisitor()); return null; } public Void visitUnaryTail(@NotNull SmalltalkParser.UnaryTailContext ctx) { log("visitUnaryTail"); ctx.unaryMessage().accept(currentVisitor()); SmalltalkParser.UnaryTailContext unaryTail = ctx.unaryTail(); if (unaryTail != null) return unaryTail.accept(currentVisitor()); return null; } public Void visitUnarySelector(@NotNull SmalltalkParser.UnarySelectorContext ctx) { log("visitUnarySelector " + ctx.IDENTIFIER().getSymbol().getText()); TerminalNode selectorNode = ctx.IDENTIFIER(); visitLine(mv, selectorNode.getSymbol().getLine()); invokePerform(mv, selectorNode.getSymbol().getText(), 0, sendToSuper); sendToSuper = false; return null; } public Void visitBinarySend(@NotNull SmalltalkParser.BinarySendContext ctx) { log("visitBinarySend"); ctx.unarySend().accept(currentVisitor()); SmalltalkParser.BinaryTailContext binaryTail = ctx.binaryTail(); if (binaryTail != null) return binaryTail.accept(currentVisitor()); return null; } public Void visitBinaryTail(@NotNull SmalltalkParser.BinaryTailContext ctx) { log("visitBinaryTail"); ctx.binaryMessage().accept(currentVisitor()); SmalltalkParser.BinaryTailContext binaryTail = ctx.binaryTail(); if (binaryTail != null) return binaryTail.accept(currentVisitor()); return null; } public Void visitKeywordSend(@NotNull SmalltalkParser.KeywordSendContext ctx) { log("visitKeywordSend"); ctx.binarySend().accept(currentVisitor()); if (referencedJVM) pushCurrentVisitor(new JVMGeneratorVisitor(cw, mv)); ctx.keywordMessage().accept(currentVisitor()); return null; } public Void visitCascade(@NotNull SmalltalkParser.CascadeContext ctx) { log("visitCascade"); SmalltalkParser.BinarySendContext binarySend = ctx.binarySend(); if (binarySend != null) binarySend.accept(currentVisitor()); SmalltalkParser.KeywordSendContext keywordSend = ctx.keywordSend(); if (keywordSend != null) keywordSend.accept(currentVisitor()); for (SmalltalkParser.MessageContext message : ctx.message()) message.accept(currentVisitor()); return null; } public Void visitAssignment(@NotNull SmalltalkParser.AssignmentContext ctx) { log("visitAssignment"); SmalltalkParser.ExpressionContext expression = ctx.expression(); if (expression == null) throw new RuntimeException("visitAssignment expression expected."); expression.accept(currentVisitor()); SmalltalkParser.VariableContext variable = ctx.variable(); if (variable == null) throw new RuntimeException("visitAssignment variable expected."); TerminalNode identifierNode = variable.IDENTIFIER(); String identifier = identifierNode.getSymbol().getText(); visitLine(mv, identifierNode.getSymbol().getLine()); if (isTemporary(identifier)) { pushDuplicate(mv); storeTemporary(mv, indexOfTemporary(identifier)); } else if (isHomeTemporary(identifier)) { pushDuplicate(mv); storeHomeTemporary(mv, indexOfHomeTemporary(identifier)); } else { // Assume the variable is an instance var. pushDuplicate(mv); storeInstVar(mv, identifier); } return null; } public Void visitMessage(@NotNull SmalltalkParser.MessageContext ctx) { log("visitMessage"); SmalltalkParser.UnaryMessageContext unaryMessage = ctx.unaryMessage(); if (unaryMessage != null) return unaryMessage.accept(currentVisitor()); SmalltalkParser.KeywordMessageContext keywordMessage = ctx.keywordMessage(); if (keywordMessage != null) return keywordMessage.accept(currentVisitor()); SmalltalkParser.BinaryMessageContext binaryMessage = ctx.binaryMessage(); if (binaryMessage != null) return binaryMessage.accept(currentVisitor()); throw new RuntimeException("visitMessage no alternative found."); } public Void visitUnaryMessage(@NotNull SmalltalkParser.UnaryMessageContext ctx) { log("visitUnaryMessage"); SmalltalkParser.UnarySelectorContext unarySelector = ctx.unarySelector(); if (unarySelector != null) unarySelector.accept(currentVisitor()); return null; } public Void visitBinaryMessage(@NotNull SmalltalkParser.BinaryMessageContext ctx) { log("visitBinaryMessage " + ctx.BINARY_SELECTOR().getSymbol().getText()); TerminalNode binarySelector = ctx.BINARY_SELECTOR(); SmalltalkParser.UnarySendContext unarySend = ctx.unarySend(); if (unarySend != null) unarySend.accept(currentVisitor()); SmalltalkParser.OperandContext operand = ctx.operand(); if (operand != null) operand.accept(currentVisitor()); visitLine(mv, binarySelector.getSymbol().getLine()); invokePerform(mv, binarySelector.getSymbol().getText(), 1, sendToSuper); sendToSuper = false; return null; } public Void visitKeywordMessage(@NotNull SmalltalkParser.KeywordMessageContext ctx) { log("visitKeywordMessage"); initializeKeyword(); initializeTryCatch(); for (SmalltalkParser.KeywordPairContext keywordPair : ctx.keywordPair()) keywordPair.accept(currentVisitor()); visitLine(mv, ctx.keywordPair().get(0).KEYWORD().getSymbol().getLine()); String keyword = removeKeyword(); setupTryBlock(); invokePerform(mv, keyword, countOf(keyword, ':'), sendToSuper); setupCatchBlock(); sendToSuper = false; return null; } private void initializeTryCatch() { tryCatchRecords = new ArrayList<>(); tryStartLabel = new Label(); tryEndLabel = new Label(); } private void setupTryBlock() { if (tryCatchRecords.isEmpty()) return; log("setupTryBlock"); for (BlockAnswerRecord record : tryCatchRecords) mv.visitTryCatchBlock(tryStartLabel, tryEndLabel, record.handlerLabel, record.exceptionName); mv.visitLabel(tryStartLabel); } private void setupCatchBlock() { if (tryCatchRecords.isEmpty()) return; log("setupCatchBlock"); for (BlockAnswerRecord record : tryCatchRecords) { mv.visitJumpInsn(GOTO, tryEndLabel); mv.visitLabel(record.handlerLabel); mv.visitMethodInsn(INVOKEVIRTUAL, record.exceptionName, "answer", "()Lst/redline/core/PrimObject;", false); mv.visitInsn(ARETURN); } mv.visitLabel(tryEndLabel); } public Void visitKeywordPair(@NotNull SmalltalkParser.KeywordPairContext ctx) { log("visitKeywordPair " + ctx.KEYWORD().getSymbol().getText()); TerminalNode keyword = ctx.KEYWORD(); String part = keyword.getSymbol().getText(); visitLine(mv, keyword.getSymbol().getLine()); addToKeyword(part); SmalltalkParser.BinarySendContext binarySend = ctx.binarySend(); if (binarySend != null) return binarySend.accept(currentVisitor()); throw new RuntimeException("visitKeywordPair binary send expected."); } public Void visitOperand(@NotNull SmalltalkParser.OperandContext ctx) { log("visitOperand"); SmalltalkParser.LiteralContext literal = ctx.literal(); if (literal != null) return literal.accept(currentVisitor()); SmalltalkParser.ReferenceContext reference = ctx.reference(); if (reference != null) return reference.accept(currentVisitor()); SmalltalkParser.SubexpressionContext subexpression = ctx.subexpression(); if (subexpression != null) return subexpression.accept(currentVisitor()); throw new RuntimeException("visitOperand no alternative found."); } public Void visitLiteral(@NotNull SmalltalkParser.LiteralContext ctx) { log("visitLiteral"); SmalltalkParser.ParsetimeLiteralContext parsetimeLiteral = ctx.parsetimeLiteral(); if (parsetimeLiteral != null) return parsetimeLiteral.accept(currentVisitor()); SmalltalkParser.RuntimeLiteralContext runtimeLiteral = ctx.runtimeLiteral(); if (runtimeLiteral != null) return runtimeLiteral.accept(currentVisitor()); throw new RuntimeException("visitLiteral no alternative found."); } public Void visitRuntimeLiteral(@NotNull SmalltalkParser.RuntimeLiteralContext ctx) { log("visitRuntimeLiteral"); SmalltalkParser.BlockContext block = ctx.block(); if (block != null) return block.accept(currentVisitor()); SmalltalkParser.DynamicDictionaryContext dynamicDictionary = ctx.dynamicDictionary(); if (dynamicDictionary != null) return dynamicDictionary.accept(currentVisitor()); SmalltalkParser.DynamicArrayContext dynamicArray = ctx.dynamicArray(); if (dynamicArray != null) return dynamicArray.accept(currentVisitor()); throw new RuntimeException("visitRuntimeLiteral no alternative found."); } public Void visitParsetimeLiteral(@NotNull SmalltalkParser.ParsetimeLiteralContext ctx) { log("visitParsetimeLiteral"); SmalltalkParser.PseudoVariableContext pseudoVariable = ctx.pseudoVariable(); if (pseudoVariable != null) return pseudoVariable.accept(currentVisitor()); SmalltalkParser.NumberContext number = ctx.number(); if (number != null) return number.accept(currentVisitor()); SmalltalkParser.CharConstantContext charConstant = ctx.charConstant(); if (charConstant != null) return charConstant.accept(currentVisitor()); SmalltalkParser.LiteralArrayContext literalArray = ctx.literalArray(); if (literalArray != null) return literalArray.accept(currentVisitor()); SmalltalkParser.StringContext string = ctx.string(); if (string != null) return string.accept(currentVisitor()); SmalltalkParser.SymbolContext symbol = ctx.symbol(); if (symbol != null) return symbol.accept(currentVisitor()); throw new RuntimeException("visitParsetimeLiteral no alternative found."); } public Void visitPseudoVariable(@NotNull SmalltalkParser.PseudoVariableContext ctx) { log("visitPseudoVariable " + ctx.RESERVED_WORD().getSymbol().getText()); TerminalNode pseudoVariable = ctx.RESERVED_WORD(); String name = pseudoVariable.getSymbol().getText(); if ("self".equals(name)) pushReceiver(mv); else if ("nil".equals(name)) pushNil(mv); else if ("true".equals(name)) pushTrue(mv); else if ("false".equals(name)) pushFalse(mv); else if ("super".equals(name)) pushSuper(mv, pseudoVariable.getSymbol().getLine()); else throw new RuntimeException("visitPseudoVariable unknown variable: " + name); return null; } public Void visitLiteralArray(@NotNull SmalltalkParser.LiteralArrayContext ctx) { log("visitLiteralArray "); pushNewObject(mv, "smalltalkArray", "", ctx.LITARR_START().getSymbol().getLine()); return visitChildren(ctx); } public Void visitLiteralArrayRest(@NotNull SmalltalkParser.LiteralArrayRestContext ctx) { log("visitLiteralArrayRest "); if (ctx.parsetimeLiteral() != null) for (SmalltalkParser.ParsetimeLiteralContext literal : ctx.parsetimeLiteral()) { throw new RuntimeException("Handle LiteralArray element"); } return null; } public Void visitCharConstant(@NotNull SmalltalkParser.CharConstantContext ctx) { log("visitCharConstant " + ctx.CHARACTER_CONSTANT().getSymbol().getText()); TerminalNode constant = ctx.CHARACTER_CONSTANT(); String value = constant.getSymbol().getText().substring(1); pushNewObject(mv, "smalltalkCharacter", value, constant.getSymbol().getLine()); return null; } public Void visitStInteger(@NotNull SmalltalkParser.StIntegerContext ctx) { log("visitInteger " + (ctx.MINUS() != null ? "-" + nodeFor(ctx.DIGIT()).getText() : nodeFor(ctx.DIGIT()).getText())); boolean minus = ctx.MINUS() != null; BasicNode number = nodeFor(ctx.DIGIT()); String value = minus ? "-" + number.getText() : number.getText(); pushNewObject(mv, "smalltalkInteger", value, number.getLine()); return null; } public Void visitStFloat(@NotNull SmalltalkParser.StFloatContext ctx) { log("visitFloat "); throw new RuntimeException("visitFloat handle me."); } public Void visitString(@NotNull SmalltalkParser.StringContext ctx) { log("visitString " + ctx.STRING().getSymbol().getText()); TerminalNode node = ctx.STRING(); String value = node.getSymbol().getText(); value = value.substring(1, value.length() - 1); pushNewObject(mv, "smalltalkString", value, node.getSymbol().getLine()); return null; } public Void visitSymbol(@NotNull SmalltalkParser.SymbolContext ctx) { log("visitSymbol #" + nodeFor(ctx).getText()); BasicNode node = nodeFor(ctx); String symbol = node.getText(); if (haveKeyword()) addArgumentToKeyword(symbol); pushNewObject(mv, "smalltalkSymbol", symbol, node.getLine()); return null; } private BasicNode nodeFor(SmalltalkParser.SymbolContext ctx) { SmalltalkParser.BareSymbolContext bareSymbolContext = ctx.bareSymbol(); TerminalNode node = bareSymbolContext.IDENTIFIER(); if (node != null) return new ExtendedTerminalNode(node, 0); node = bareSymbolContext.BINARY_SELECTOR(); if (node != null) return new ExtendedTerminalNode(node, 0); List<TerminalNode> keywords = bareSymbolContext.KEYWORD(); if (keywords != null && !keywords.isEmpty()) return nodeFor(keywords); List<TerminalNode> pipe = bareSymbolContext.PIPE(); if (pipe != null && !pipe.isEmpty()) return nodeFor(pipe); throw new RuntimeException("Node cannot be determined from context."); } private BasicNode nodeFor(List<TerminalNode> nodes) { int line = nodes.get(0).getSymbol().getLine(); StringBuilder text = new StringBuilder(); for (TerminalNode n : nodes) text.append(n.getSymbol().getText()); return new BasicNode(line, text.toString(), 0); } public Void visitReference(@NotNull SmalltalkParser.ReferenceContext ctx) { log("visitReference " + ctx.variable().IDENTIFIER().getSymbol().getText()); TerminalNode identifier = ctx.variable().IDENTIFIER(); String name = identifier.getSymbol().getText(); visitLine(mv, identifier.getSymbol().getLine()); if (isTemporary(name)) pushTemporary(mv, indexOfTemporary(name)); else if (isArgument(name)) pushArgument(mv, indexOfArgument(name)); else if ("JVM".equals(name)) referencedJVM = true; else if (isHomeTemporary(name)) pushHomeTemporary(mv, indexOfHomeTemporary(name)); else if (isHomeArgument(name)) pushHomeArgument(mv, indexOfHomeArgument(name)); else if (isOuterArgument(name)) pushOuterArgument(mv, indexOfOuterArgument(name)); else if (Character.isUpperCase(name.codePointAt(0))) pushReference(mv, name); // May be Class reference or InstVar. else pushInstVar(mv, name); // Assume instVar. return null; } public Void visitBlock(@NotNull SmalltalkParser.BlockContext ctx) { log("visitBlock " + peekKeyword() + " " + blockNumber); KeywordRecord keywordRecord = peekKeyword(); String name = makeBlockMethodName(keywordRecord); boolean methodBlock = keywordRecord.keyword.toString().endsWith("withMethod:"); HashMap<String, ExtendedTerminalNode> homeTemps = homeTemporaries; if (homeTemps == null && !methodBlock) homeTemps = temporaries; HashMap<String, ExtendedTerminalNode> homeArgs = homeArguments; if (homeArgs == null && !methodBlock) homeArgs = arguments; BlockGeneratorVisitor blockGeneratorVisitor = new BlockGeneratorVisitor(cw, name, blockNumber, homeTemps, homeArgs, arguments); pushCurrentVisitor(blockGeneratorVisitor); blockGeneratorVisitor.handleBlock(ctx); blockNumber = blockGeneratorVisitor.blockNumber; removeJVMGeneratorVisitor(); popCurrentVisitor(); int line = ctx.BLOCK_START().getSymbol().getLine(); if (methodBlock) pushNewMethod(mv, fullClassName(), name, LAMBDA_BLOCK_SIG, line); else { String blockAnswerClassName = makeBlockAnswerClassName(name); pushNewBlock(mv, fullClassName(), name, LAMBDA_BLOCK_SIG, line, blockGeneratorVisitor.isAnswerBlock(), blockAnswerClassName); if (blockGeneratorVisitor.isAnswerBlock()) { loadBlockAnswerClass(blockAnswerClassName); tryCatchRecords.add(new BlockAnswerRecord(blockAnswerClassName)); } } return null; } private void loadBlockAnswerClass(String blockAnswerClassName) { log("loadBlockAnswerClass: " + blockAnswerClassName); byte[] classBytes = createBlockAnswerClass(blockAnswerClassName); SmalltalkClassLoader classLoader = classLoader(); classLoader.defineClass(classBytes); } private SmalltalkClassLoader classLoader() { return (SmalltalkClassLoader) Thread.currentThread().getContextClassLoader(); } private byte[] createBlockAnswerClass(String blockAnswerClassName) { String className = blockAnswerClassName.replaceAll("\\.", "/"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); MethodVisitor mv; cw.visit(BYTECODE_VERSION, ACC_PUBLIC + ACC_SUPER, className, null, "st/redline/core/PrimBlockAnswer", null); cw.visitSource(className() + sourceFileExtension(), null); mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Lst/redline/core/PrimObject;)V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKESPECIAL, "st/redline/core/PrimBlockAnswer", "<init>", "(Lst/redline/core/PrimObject;)V", false); mv.visitInsn(RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); cw.visitEnd(); return cw.toByteArray(); } public Void visitSubexpression(@NotNull SmalltalkParser.SubexpressionContext ctx) { log("visitSubexpression"); // 2 + ( (3 * 4) - 1 ) // evaluated last ( (evaluated first) evaluated second ) SmalltalkParser.ExpressionContext expression = ctx.expression(); expression.accept(currentVisitor()); return null; } public Void visitVariable(@NotNull SmalltalkParser.VariableContext ctx) { throw new RuntimeException("visitVariable should have been handed before now."); } private String makeBlockAnswerClassName(String blockName) { return packageName() + '.' + className() + "$A" + blockName; } private String makeBlockMethodName(KeywordRecord keywordRecord) { StringBuilder name = new StringBuilder(); if (keywordRecord.firstArgument.length() == 0) { blockNumber++; name.append("B").append(blockNumber); } else name.append(keywordRecord.firstArgument.toString()); return name.toString(); } } private class BlockGeneratorVisitor extends ClassGeneratorVisitor { private final ClassWriter cw; private String blockName; private boolean returnRequired; public BlockGeneratorVisitor(ClassWriter cw, String name, int blockNumber, HashMap<String, ExtendedTerminalNode> homeTemporaries, HashMap<String, ExtendedTerminalNode> homeArguments, HashMap<String, ExtendedTerminalNode> outerArguments) { super(cw); this.cw = cw; this.blockName = name; this.returnRequired = false; this.blockNumber = blockNumber; this.homeTemporaries = homeTemporaries; this.homeArguments = homeArguments; this.outerArguments = outerArguments; } public void handleBlock(@NotNull SmalltalkParser.BlockContext ctx) { log("handleBlock " + blockName + " " + blockNumber); openBlockLambdaMethod(); SmalltalkParser.BlockParamListContext blockParamList = ctx.blockParamList(); if (blockParamList != null) blockParamList.accept(currentVisitor()); SmalltalkParser.SequenceContext blockSequence = ctx.sequence(); if (blockSequence != null) blockSequence.accept(currentVisitor()); returnRequired = returnRequired(blockSequence); closeBlockLambdaMethod(returnRequired); } public boolean isAnswerBlock() { return !returnRequired; } private boolean returnRequired(SmalltalkParser.SequenceContext blockSequence) { if (blockSequence == null) return true; SmalltalkParser.StatementsContext statements = blockSequence.statements(); if (statements == null) { // We have no statements in block, so answer nil. pushNil(mv); return true; } List<ParseTree> list = statements.children; for (ParseTree parseTree : list) { if (parseTree instanceof SmalltalkParser.AnswerContext) return false; } return true; } public Void visitBlockParamList(@NotNull SmalltalkParser.BlockParamListContext ctx) { log("visitBlockParamList"); int index = 0; int n = ctx.getChildCount(); for (int i = 0; i < n; ++i) { ParseTree c = ctx.getChild(i); if (c instanceof TerminalNode) addArgumentToMap(new ExtendedTerminalNode((TerminalNode) c, index++)); } return null; } private void closeBlockLambdaMethod(boolean returnRequired) { log("closeBlockLambdaMethod: " + blockName + " " + returnRequired); if (returnRequired) mv.visitInsn(ARETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } private void openBlockLambdaMethod() { log("openBlockLambdaMethod: " + blockName); mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, blockName, LAMBDA_BLOCK_SIG, null, null); mv.visitCode(); } } private class JVMGeneratorVisitor extends ClassGeneratorVisitor { private final MethodVisitor mv; private List<Object> arguments = new ArrayList<Object>(); public JVMGeneratorVisitor(ClassWriter cw, MethodVisitor mv) { super(cw, mv); this.mv = mv; } public Void visitMessage(@NotNull SmalltalkParser.MessageContext ctx) { log("visitMessage"); SmalltalkParser.KeywordMessageContext keywordMessage = ctx.keywordMessage(); if (keywordMessage != null) return keywordMessage.accept(currentVisitor()); throw new RuntimeException("visitMessage no alternative found."); } public Void visitKeywordMessage(@NotNull SmalltalkParser.KeywordMessageContext ctx) { log("visitKeywordMessage"); initializeKeyword(); for (SmalltalkParser.KeywordPairContext keywordPair : ctx.keywordPair()) keywordPair.accept(currentVisitor()); visitLine(mv, ctx.keywordPair().get(0).KEYWORD().getSymbol().getLine()); String keyword = removeKeyword(); JVMWriter jvmWriter = JVM_WRITERS.get(keyword); if (jvmWriter == null) throw new RuntimeException("JVM keyword not recognized."); jvmWriter.write(mv, arguments); return null; } protected void initializeKeyword() { super.initializeKeyword(); arguments = new ArrayList<>(); } public Void visitKeywordPair(@NotNull SmalltalkParser.KeywordPairContext ctx) { log("visitKeywordPair " + ctx.KEYWORD().getSymbol().getText()); TerminalNode keyword = ctx.KEYWORD(); String part = keyword.getSymbol().getText(); visitLine(mv, keyword.getSymbol().getLine()); addToKeyword(part); SmalltalkParser.BinarySendContext binarySend = ctx.binarySend(); arguments.add(argumentFrom(binarySend)); return null; } private Object argumentFrom(SmalltalkParser.BinarySendContext binarySend) { SmalltalkParser.UnarySendContext unarySend = binarySend.unarySend(); if (unarySend != null) { SmalltalkParser.OperandContext operand = unarySend.operand(); if (operand != null) { SmalltalkParser.LiteralContext literal = operand.literal(); if (literal != null) { SmalltalkParser.ParsetimeLiteralContext parsetimeLiteral = literal.parsetimeLiteral(); if (parsetimeLiteral != null) { SmalltalkParser.StringContext string = parsetimeLiteral.string(); if (string != null) { String raw = string.STRING().getSymbol().getText(); return raw.substring(1, raw.length() - 1); } SmalltalkParser.NumberContext number = parsetimeLiteral.number(); if (number != null) { return number.getText(); } SmalltalkParser.SymbolContext symbol = parsetimeLiteral.symbol(); // if (symbol != null) { // SmalltalkParser.BareSymbolContext bareSymbol = symbol.bareSymbol(); // List<TerminalNode> keyword = bareSymbol.KEYWORD(); // if (keyword != null) { // System.out.println("here"); // } // } throw new RuntimeException("Unhandled JVM keyword argument."); } } } } throw new RuntimeException("JVM keyword argument expected."); } } private class BasicNode { private final int index; private final int line; private final String text; public BasicNode(int line, String text, int index) { this.line = line; this.text = text; this.index = index; } public int getIndex() { return index; } public int getLine() { return line; } public String getText() { return text; } } private class ExtendedTerminalNode extends BasicNode { public ExtendedTerminalNode(TerminalNode node, int index) { super(node.getSymbol().getLine(), node.getSymbol().getText(), index); } } private class BlockAnswerRecord { public final String exceptionName; public Label handlerLabel; public BlockAnswerRecord(String name) { this.exceptionName = name.replaceAll("\\.", "/"); this.handlerLabel = new Label(); } } private class KeywordRecord { public StringBuilder keyword = new StringBuilder(); public StringBuilder firstArgument = new StringBuilder(); public String toString() { return keyword + " - " + firstArgument; } } private interface JVMWriter { void write(MethodVisitor mv, List<Object> arguments); } private static final Map<String, JVMWriter> JVM_WRITERS = new HashMap<String, JVMWriter>(); static { JVM_WRITERS.put("aload:", new JVMWriter() { public void write(MethodVisitor mv, List<Object> arguments) { mv.visitVarInsn(ALOAD, Integer.valueOf(String.valueOf(arguments.get(0)))); } }); JVM_WRITERS.put("invokeVirtual:method:matching:", new JVMWriter() { public void write(MethodVisitor mv, List<Object> arguments) { mv.visitMethodInsn(INVOKEVIRTUAL, String.valueOf(arguments.get(0)), String.valueOf(arguments.get(1)), String.valueOf(arguments.get(2)), false); } }); JVM_WRITERS.put("invokeStatic:method:matching:", new JVMWriter() { public void write(MethodVisitor mv, List<Object> arguments) { mv.visitMethodInsn(INVOKESTATIC, String.valueOf(arguments.get(0)), String.valueOf(arguments.get(1)), String.valueOf(arguments.get(2)), false); } }); JVM_WRITERS.put("getStatic:named:as:", new JVMWriter() { public void write(MethodVisitor mv, List<Object> arguments) { mv.visitFieldInsn(GETSTATIC, String.valueOf(arguments.get(0)), String.valueOf(arguments.get(1)), String.valueOf(arguments.get(2))); } }); // ## Redline Additions - these are not true JVM instructions but helpers. JVM_WRITERS.put("argLoad:", new JVMWriter() { public void write(MethodVisitor mv, List<Object> arguments) { mv.visitVarInsn(ALOAD, 2); // Load Context pushNumber(mv, Integer.valueOf(String.valueOf(arguments.get(0)))); // Load index of argument we want to get. mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimContext", "argumentAt", "(I)Lst/redline/core/PrimObject;", false); } }); JVM_WRITERS.put("primitive:", new JVMWriter() { public void write(MethodVisitor mv, List<Object> arguments) { mv.visitVarInsn(ALOAD, 1); // Load Receiver mv.visitVarInsn(ALOAD, 2); // Load Context String methodName = "primitive" + String.valueOf(arguments.get(0)); mv.visitMethodInsn(INVOKEVIRTUAL, "st/redline/core/PrimObject", methodName, "(Lst/redline/core/PrimContext;)Lst/redline/core/PrimObject;", false); } }); } static { OPCODES.put("V1_1", 196653); OPCODES.put("V1_2", 46); OPCODES.put("V1_3", 47); OPCODES.put("V1_4", 48); OPCODES.put("V1_5", 49); OPCODES.put("V1_6", 50); OPCODES.put("V1_7", 51); OPCODES.put("V1_7", 51); OPCODES.put("V1_8", 52); OPCODES.put("ACC_PUBLIC", 1); OPCODES.put("ACC_PRIVATE", 2); OPCODES.put("ACC_PROTECTED", 4); OPCODES.put("ACC_STATIC", 8); OPCODES.put("ACC_FINAL", 16); OPCODES.put("ACC_SUPER", 32); OPCODES.put("ACC_SYNCHRONIZED", 32); OPCODES.put("ACC_VOLATILE", 64); OPCODES.put("ACC_BRIDGE", 64); OPCODES.put("ACC_VARARGS", 128); OPCODES.put("ACC_TRANSIENT", 128); OPCODES.put("ACC_NATIVE", 256); OPCODES.put("ACC_INTERFACE", 512); OPCODES.put("ACC_ABSTRACT", 1024); OPCODES.put("ACC_STRICT", 2048); OPCODES.put("ACC_SYNTHETIC", 4096); OPCODES.put("ACC_ANNOTATION", 8192); OPCODES.put("ACC_ENUM", 16384); OPCODES.put("ACC_DEPRECATED", 131072); OPCODES.put("T_BOOLEAN", 4); OPCODES.put("T_CHAR", 5); OPCODES.put("T_FLOAT", 6); OPCODES.put("T_DOUBLE", 7); OPCODES.put("T_BYTE", 8); OPCODES.put("T_SHORT", 9); OPCODES.put("T_INT", 10); OPCODES.put("T_LONG", 11); OPCODES.put("F_NEW", -1); OPCODES.put("F_FULL", 0); OPCODES.put("F_APPEND", 1); OPCODES.put("F_CHOP", 2); OPCODES.put("F_SAME", 3); OPCODES.put("F_SAME1", 4); OPCODES.put("TOP", TOP); OPCODES.put("INTEGER", INTEGER); OPCODES.put("FLOAT", FLOAT); OPCODES.put("DOUBLE", DOUBLE); OPCODES.put("LONG", LONG); OPCODES.put("NULL", NULL); OPCODES.put("UNINITIALIZED_THIS", UNINITIALIZED_THIS); OPCODES.put("NOP", 0); OPCODES.put("ACONST_NULL", 1); OPCODES.put("ICONST_M1", 2); OPCODES.put("ICONST_0", 3); OPCODES.put("ICONST_1", 4); OPCODES.put("ICONST_2", 5); OPCODES.put("ICONST_3", 6); OPCODES.put("ICONST_4", 7); OPCODES.put("ICONST_5", 8); OPCODES.put("LCONST_0", 9); OPCODES.put("LCONST_1", 10); OPCODES.put("FCONST_0", 11); OPCODES.put("FCONST_1", 12); OPCODES.put("FCONST_2", 13); OPCODES.put("DCONST_0", 14); OPCODES.put("DCONST_1", 15); OPCODES.put("BIPUSH", 16); OPCODES.put("SIPUSH", 17); OPCODES.put("LDC", 18); OPCODES.put("ILOAD", 21); OPCODES.put("LLOAD", 22); OPCODES.put("FLOAD", 23); OPCODES.put("DLOAD", 24); OPCODES.put("ALOAD", 25); OPCODES.put("IALOAD", 46); OPCODES.put("LALOAD", 47); OPCODES.put("FALOAD", 48); OPCODES.put("DALOAD", 49); OPCODES.put("AALOAD", 50); OPCODES.put("BALOAD", 51); OPCODES.put("CALOAD", 52); OPCODES.put("SALOAD", 53); OPCODES.put("ISTORE", 54); OPCODES.put("LSTORE", 55); OPCODES.put("FSTORE", 56); OPCODES.put("DSTORE", 57); OPCODES.put("ASTORE", 58); OPCODES.put("IASTORE", 79); OPCODES.put("LASTORE", 80); OPCODES.put("FASTORE", 81); OPCODES.put("DASTORE", 82); OPCODES.put("AASTORE", 83); OPCODES.put("BASTORE", 84); OPCODES.put("CASTORE", 85); OPCODES.put("SASTORE", 86); OPCODES.put("POP", 87); OPCODES.put("POP2", 88); OPCODES.put("DUP", 89); OPCODES.put("DUP_X1", 90); OPCODES.put("DUP_X2", 91); OPCODES.put("DUP2", 92); OPCODES.put("DUP2_X1", 93); OPCODES.put("DUP2_X2", 94); OPCODES.put("SWAP", 95); OPCODES.put("IADD", 96); OPCODES.put("LADD", 97); OPCODES.put("FADD", 98); OPCODES.put("DADD", 99); OPCODES.put("ISUB", 100); OPCODES.put("LSUB", 101); OPCODES.put("FSUB", 102); OPCODES.put("DSUB", 103); OPCODES.put("IMUL", 104); OPCODES.put("LMUL", 105); OPCODES.put("FMUL", 106); OPCODES.put("DMUL", 107); OPCODES.put("IDIV", 108); OPCODES.put("LDIV", 109); OPCODES.put("FDIV", 110); OPCODES.put("DDIV", 111); OPCODES.put("IREM", 112); OPCODES.put("LREM", 113); OPCODES.put("FREM", 114); OPCODES.put("DREM", 115); OPCODES.put("INEG", 116); OPCODES.put("LNEG", 117); OPCODES.put("FNEG", 118); OPCODES.put("DNEG", 119); OPCODES.put("ISHL", 120); OPCODES.put("LSHL", 121); OPCODES.put("ISHR", 122); OPCODES.put("LSHR", 123); OPCODES.put("IUSHR", 124); OPCODES.put("LUSHR", 125); OPCODES.put("IAND", 126); OPCODES.put("LAND", 127); OPCODES.put("IOR", 128); OPCODES.put("LOR", 129); OPCODES.put("IXOR", 130); OPCODES.put("LXOR", 131); OPCODES.put("IINC", 132); OPCODES.put("I2L", 133); OPCODES.put("I2F", 134); OPCODES.put("I2D", 135); OPCODES.put("L2I", 136); OPCODES.put("L2F", 137); OPCODES.put("L2D", 138); OPCODES.put("F2I", 139); OPCODES.put("F2L", 140); OPCODES.put("F2D", 141); OPCODES.put("D2I", 142); OPCODES.put("D2L", 143); OPCODES.put("D2F", 144); OPCODES.put("I2B", 145); OPCODES.put("I2C", 146); OPCODES.put("I2S", 147); OPCODES.put("LCMP", 148); OPCODES.put("FCMPL", 149); OPCODES.put("FCMPG", 150); OPCODES.put("DCMPL", 151); OPCODES.put("DCMPG", 152); OPCODES.put("IFEQ", 153); OPCODES.put("IFNE", 154); OPCODES.put("IFLT", 155); OPCODES.put("IFGE", 156); OPCODES.put("IFGT", 157); OPCODES.put("IFLE", 158); OPCODES.put("IF_ICMPEQ", 159); OPCODES.put("IF_ICMPNE", 160); OPCODES.put("IF_ICMPLT", 161); OPCODES.put("IF_ICMPGE", 162); OPCODES.put("IF_ICMPGT", 163); OPCODES.put("IF_ICMPLE", 164); OPCODES.put("IF_ACMPEQ", 165); OPCODES.put("IF_ACMPNE", 166); OPCODES.put("GOTO", 167); OPCODES.put("JSR", 168); OPCODES.put("RET", 169); OPCODES.put("TABLESWITCH", 170); OPCODES.put("LOOKUPSWITCH", 171); OPCODES.put("IRETURN", 172); OPCODES.put("LRETURN", 173); OPCODES.put("FRETURN", 174); OPCODES.put("DRETURN", 175); OPCODES.put("ARETURN", 176); OPCODES.put("RETURN", 177); OPCODES.put("GETSTATIC", 178); OPCODES.put("PUTSTATIC", 179); OPCODES.put("GETFIELD", 180); OPCODES.put("PUTFIELD", 181); OPCODES.put("INVOKEVIRTUAL", 182); OPCODES.put("INVOKESPECIAL", 183); OPCODES.put("INVOKESTATIC", 184); OPCODES.put("INVOKEINTERFACE", 185); OPCODES.put("INVOKEDYNAMIC", 186); OPCODES.put("NEW", 187); OPCODES.put("NEWARRAY", 188); OPCODES.put("ANEWARRAY", 189); OPCODES.put("ARRAYLENGTH", 190); OPCODES.put("ATHROW", 191); OPCODES.put("CHECKCAST", 192); OPCODES.put("INSTANCEOF", 193); OPCODES.put("MONITORENTER", 194); OPCODES.put("MONITOREXIT", 195); OPCODES.put("MULTIANEWARRAY", 197); OPCODES.put("IFNULL", 198); OPCODES.put("IFNONNULL", 199); } }