From 2a0257312245f909d7133541efa2b39492a119a9 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Wed, 10 Apr 2024 22:52:35 +0800
Subject: [PATCH 01/12] Update ControlBarChapterSelect with Java

---
 src/commons/controlBar/ControlBarChapterSelect.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/commons/controlBar/ControlBarChapterSelect.tsx b/src/commons/controlBar/ControlBarChapterSelect.tsx
index aad0e84e19..47ba376344 100644
--- a/src/commons/controlBar/ControlBarChapterSelect.tsx
+++ b/src/commons/controlBar/ControlBarChapterSelect.tsx
@@ -9,6 +9,7 @@ import {
   fullJSLanguage,
   fullTSLanguage,
   htmlLanguage,
+  javaLanguages,
   pyLanguages,
   SALanguage,
   schemeLanguages,
@@ -87,7 +88,8 @@ export const ControlBarChapterSelect: React.FC<ControlBarChapterSelectProps> = (
     // See https://github.com/source-academy/frontend/pull/2460#issuecomment-1528759912
     ...(Constants.playgroundOnly ? [fullJSLanguage, fullTSLanguage, htmlLanguage] : []),
     ...schemeLanguages,
-    ...pyLanguages
+    ...pyLanguages,
+    ...javaLanguages,
   ];
 
   return (

From bb1f2b3f43d86c9cb439d345fbff37af7769d111 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Wed, 10 Apr 2024 22:54:13 +0800
Subject: [PATCH 02/12] Implement Java CSEC Visualizer

---
 src/features/cseMachine/java/CseMachine.tsx   | 155 ++++++++++++++
 .../cseMachine/java/components/Arrow.tsx      |  72 +++++++
 .../cseMachine/java/components/Binding.tsx    |  98 +++++++++
 .../cseMachine/java/components/Control.tsx    | 202 ++++++++++++++++++
 .../java/components/ControlItem.tsx           | 154 +++++++++++++
 .../java/components/Environment.tsx           | 189 ++++++++++++++++
 .../cseMachine/java/components/Frame.tsx      | 150 +++++++++++++
 .../cseMachine/java/components/Line.tsx       |  62 ++++++
 .../cseMachine/java/components/Method.tsx     | 119 +++++++++++
 .../cseMachine/java/components/Object.tsx     |  44 ++++
 .../cseMachine/java/components/Stash.tsx      |  90 ++++++++
 .../cseMachine/java/components/StashItem.tsx  |  95 ++++++++
 .../cseMachine/java/components/Text.tsx       |  60 ++++++
 .../cseMachine/java/components/Variable.tsx   | 109 ++++++++++
 14 files changed, 1599 insertions(+)
 create mode 100644 src/features/cseMachine/java/CseMachine.tsx
 create mode 100644 src/features/cseMachine/java/components/Arrow.tsx
 create mode 100644 src/features/cseMachine/java/components/Binding.tsx
 create mode 100644 src/features/cseMachine/java/components/Control.tsx
 create mode 100644 src/features/cseMachine/java/components/ControlItem.tsx
 create mode 100644 src/features/cseMachine/java/components/Environment.tsx
 create mode 100644 src/features/cseMachine/java/components/Frame.tsx
 create mode 100644 src/features/cseMachine/java/components/Line.tsx
 create mode 100644 src/features/cseMachine/java/components/Method.tsx
 create mode 100644 src/features/cseMachine/java/components/Object.tsx
 create mode 100644 src/features/cseMachine/java/components/Stash.tsx
 create mode 100644 src/features/cseMachine/java/components/StashItem.tsx
 create mode 100644 src/features/cseMachine/java/components/Text.tsx
 create mode 100644 src/features/cseMachine/java/components/Variable.tsx

diff --git a/src/features/cseMachine/java/CseMachine.tsx b/src/features/cseMachine/java/CseMachine.tsx
new file mode 100644
index 0000000000..9893220420
--- /dev/null
+++ b/src/features/cseMachine/java/CseMachine.tsx
@@ -0,0 +1,155 @@
+import { Context } from "java-slang/dist/ec-evaluator/types";
+import { KonvaEventObject } from "konva/lib/Node";
+import React, { RefObject } from "react";
+import { Layer, Rect, Stage } from "react-konva";
+
+import { Config, ShapeDefaultProps } from "./../CseMachineConfig";
+import { Control } from "./components/Control";
+import { Environment } from "./components/Environment";
+import { Stash } from "./components/Stash";
+
+type SetVis = (vis: React.ReactNode) => void;
+type SetEditorHighlightedLines = (segments: [number, number][]) => void;
+
+export class CseMachine {
+  /** the unique key assigned to each node */
+  static key: number = 0;
+
+  /** callback function to update the visualization state in the SideContentCseMachine component */
+  private static setVis: SetVis;
+  /** function to highlight editor lines */
+  public static setEditorHighlightedLines: SetEditorHighlightedLines;
+
+  public static stageRef: RefObject<any> = React.createRef();
+  /** scale factor for zooming and out of canvas */
+  public static scaleFactor = 1.02;
+
+  static environment: Environment | undefined;
+  static control: Control | undefined;
+  static stash: Stash | undefined;
+
+  static init(
+    setVis: SetVis,
+    setEditorHighlightedLines: (segments: [number, number][]) => void,
+  ) {
+    this.setVis = setVis;
+    this.setEditorHighlightedLines = setEditorHighlightedLines;
+  }
+
+  /** updates the visualization state in the SideContentCseMachine component based on
+   * the Java Slang context passed in */
+  static drawCse(context: Context) {
+    if (!this.setVis || !context.environment || !context.control || !context.stash) {
+      throw new Error('Java CSE Machine not initialized');
+    }
+
+    CseMachine.environment = new Environment(context.environment);
+    CseMachine.control = new Control(context.control);
+    CseMachine.stash = new Stash(context.stash);
+
+    this.setVis(this.draw());
+
+    // Set icon to blink.
+    const icon = document.getElementById('env_visualizer-icon');
+    icon && icon.classList.add('side-content-tab-alert');
+  }
+
+  static clearCse() {
+    if (this.setVis) {
+      this.setVis(undefined);
+      CseMachine.environment = undefined;
+      CseMachine.control = undefined;
+      CseMachine.stash = undefined;
+    }
+  }
+
+  /**
+   * Updates the scale of the stage after the user inititates a zoom in or out
+   * by scrolling or by the trackpad.
+   */
+  static zoomStage(event: KonvaEventObject<WheelEvent> | boolean, multiplier: number = 1) {
+    typeof event != 'boolean' && event.evt.preventDefault();
+    if (CseMachine.stageRef.current) {
+      const stage = CseMachine.stageRef.current;
+      const oldScale = stage.scaleX();
+      const { x: pointerX, y: pointerY } = stage.getPointerPosition();
+      const mousePointTo = {
+        x: (pointerX - stage.x()) / oldScale,
+        y: (pointerY - stage.y()) / oldScale
+      };
+
+      // zoom in or zoom out
+      const direction =
+        typeof event != 'boolean' ? (event.evt.deltaY > 0 ? -1 : 1) : event ? 1 : -1;
+
+      // Check if the zoom limits have been reached
+      if ((direction > 0 && oldScale < 3) || (direction < 0 && oldScale > 0.4)) {
+        const newScale =
+          direction > 0
+            ? oldScale * CseMachine.scaleFactor ** multiplier
+            : oldScale / CseMachine.scaleFactor ** multiplier;
+        stage.scale({ x: newScale, y: newScale });
+        if (typeof event !== 'boolean') {
+          const newPos = {
+            x: pointerX - mousePointTo.x * newScale,
+            y: pointerY - mousePointTo.y * newScale
+          };
+          stage.position(newPos);
+          stage.batchDraw();
+        }
+      }
+    }
+  }
+
+  static draw(): React.ReactNode {
+    const layout = (
+      <div className={'sa-cse-machine'} data-testid="sa-cse-machine">
+        <div
+          id="scroll-container"
+          style={{
+            width: window.innerWidth - 50,
+            height: window.innerHeight - 150,
+            overflow: 'hidden'
+          }}
+        >
+          <div
+            id="large-container"
+            style={{
+              width: Config.CanvasMinWidth,
+              height: Config.CanvasMinHeight,
+              overflow: 'hidden',
+              backgroundColor: Config.SA_BLUE
+            }}
+          >
+            <Stage
+              width={+Config.CanvasMinWidth}
+              height={+Config.CanvasMinHeight}
+              ref={this.stageRef}
+              draggable
+              onWheel={CseMachine.zoomStage}
+              className="draggable"
+            >
+              <Layer>
+                <Rect
+                  {...ShapeDefaultProps}
+                  x={0}
+                  y={0}
+                  width={Config.CanvasMinWidth}
+                  height={Config.CanvasMinHeight}
+                  fill={Config.SA_BLUE}
+                  key={CseMachine.key++}
+                  listening={false}
+                />
+                {this.control?.draw()}
+                {this.stash?.draw()}
+                {this.environment?.draw()}
+              </Layer>
+            </Stage>
+          </div>
+        </div>
+      </div>
+    );
+
+    return layout;
+  }
+}
diff --git a/src/features/cseMachine/java/components/Arrow.tsx b/src/features/cseMachine/java/components/Arrow.tsx
new file mode 100644
index 0000000000..51de860418
--- /dev/null
+++ b/src/features/cseMachine/java/components/Arrow.tsx
@@ -0,0 +1,72 @@
+import { KonvaEventObject } from 'konva/lib/Node';
+import { Arrow as KonvaArrow, Group as KonvaGroup, Path as KonvaPath } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { IHoverable } from '../../CseMachineTypes';
+import {
+  setHoveredCursor,
+  setHoveredStyle,
+  setUnhoveredCursor,
+  setUnhoveredStyle,
+} from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+
+/** this class encapsulates an Arrow to be drawn between 2 points */
+export class Arrow extends Visible implements IHoverable {
+  private static readonly TO_X_INDEX = 2;
+  private readonly _points: number[] = [];
+
+  constructor(
+    fromX: number,
+    fromY: number,
+    toX: number,
+    toY: number,
+  ) {
+    super();
+    this._points.push(fromX, fromY, toX, toY);
+  }
+
+  setToX(x: number) {
+    this._points[Arrow.TO_X_INDEX] = x;
+  }
+  
+  onMouseEnter(e: KonvaEventObject<MouseEvent>) {
+    setHoveredStyle(e.currentTarget)
+    setHoveredCursor(e.currentTarget);
+  }
+
+  onMouseLeave(e: KonvaEventObject<MouseEvent>) {
+    setUnhoveredStyle(e.currentTarget);
+    setUnhoveredCursor(e.currentTarget);
+  }
+
+  draw() {
+    const path = `M ${this._points[0]} ${this._points[1]} L ${this._points[2]} ${this._points[3]}`;
+    return (
+      <KonvaGroup
+        key={CseMachine.key++}
+        onMouseEnter={e => this.onMouseEnter(e)}
+        onMouseLeave={e => this.onMouseLeave(e)}
+      >
+        <KonvaPath
+          {...ShapeDefaultProps}
+          stroke={String(Config.SA_WHITE)}
+          strokeWidth={Number(Config.ArrowStrokeWidth)}
+          hitStrokeWidth={Number(Config.ArrowHitStrokeWidth)}
+          data={path}
+          key={CseMachine.key++}
+        />
+        <KonvaArrow
+          {...ShapeDefaultProps}
+          points={this._points.slice(this._points.length - 4)}
+          fill={String(Config.SA_WHITE)}
+          strokeEnabled={false}
+          pointerWidth={Number(Config.ArrowHeadSize)}
+          pointerLength={Number(Config.ArrowHeadSize)}
+          key={CseMachine.key++}
+        />
+      </KonvaGroup>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Binding.tsx b/src/features/cseMachine/java/components/Binding.tsx
new file mode 100644
index 0000000000..2a20032c9e
--- /dev/null
+++ b/src/features/cseMachine/java/components/Binding.tsx
@@ -0,0 +1,98 @@
+import { Name, StructType, Value } from 'java-slang/dist/ec-evaluator/types';
+import React from 'react';
+
+import { Visible } from '../../components/Visible';
+import { Config } from '../../CseMachineConfig';
+import { CseMachine } from '../CseMachine';
+import { Arrow } from './Arrow';
+import { Method } from './Method';
+import { Text } from './Text';
+import { Variable } from './Variable';
+
+/** a Binding is a key-value pair in a Frame */
+export class Binding extends Visible {
+  private readonly _name: Text;
+  
+  private readonly _value: Variable | Method | Text;
+  // Only Method has arrow.
+  private readonly _arrow: Arrow | undefined;
+
+  constructor(
+    name: Name, 
+    value: Value,
+    x: number,
+    y: number
+  ) {
+    super();
+
+    // Position.
+    this._x = x;
+    this._y = y;
+
+    if (value.kind === StructType.CLOSURE) {
+      // Name.
+      this._name = new Text(
+        name + Config.VariableColon, // := is part of name
+        this.x(),
+        this.y());
+      // Value.
+      this._value = new Method(
+        this._name.x() + this._name.width(),
+        this.y() + this._name.height() / 2,
+        value);
+      this._arrow = new Arrow(
+        this._name.x() + this._name.width(),
+        this._name.y() + this._name.height() / 2,
+        this._value.x(),
+        this._value.y());
+    } else if (value.kind === StructType.VARIABLE) {
+      // Name.
+      this._name = new Text(
+        name + Config.VariableColon, // := is part of name
+        this.x(),
+        this.y() + Config.FontSize + Config.TextPaddingX);
+      // Value.
+      this._value = new Variable(
+        this._name.x() + this._name.width(),
+        this.y(),
+        value);
+    } else /*if (value.kind === StructType.CLASS)*/ {
+      // Dummy value as class will nvr be drawn.
+      // Name.
+      this._name = new Text(
+        name + Config.VariableColon, // := is part of name
+        this.x(),
+        this.y() + Config.FontSize + Config.TextPaddingX);
+      // Value.
+      this._value = new Text(
+        "",
+        this._name.x() + this._name.width() + Config.TextPaddingX,
+        this.y() + Config.TextPaddingX);
+    }
+
+    // Height and width.
+    this._height = Math.max(this._name.height(), this._value.height());
+    this._width = this._value.x() + this._value.width() - this._name.x();
+  }
+
+  get value() {
+    return this._value;
+  }
+
+  setArrowToX(x: number) {
+    this._arrow?.setToX(x);
+  }
+
+  draw(): React.ReactNode {
+    return (
+      <React.Fragment key={CseMachine.key++}>
+        {/* Name */}
+        {this._name.draw()}
+
+        {/* Value */}
+        {this._value.draw()}
+        {this._arrow?.draw()}
+      </React.Fragment>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Control.tsx b/src/features/cseMachine/java/components/Control.tsx
new file mode 100644
index 0000000000..7d2a7706bc
--- /dev/null
+++ b/src/features/cseMachine/java/components/Control.tsx
@@ -0,0 +1,202 @@
+import { astToString } from "java-slang/dist/ast/utils/astToString";
+import { Control as JavaControl } from "java-slang/dist/ec-evaluator/components"; 
+import {
+  BinOpInstr,
+  ControlItem as JavaControlItem,
+  EnvInstr,
+  EvalVarInstr,
+  InstrType,
+  InvInstr,
+  NewInstr,
+  ResConOverloadInstr,
+  ResInstr,
+  ResOverloadInstr,
+  ResTypeContInstr,
+  ResTypeInstr,
+} from "java-slang/dist/ec-evaluator/types";
+import { isInstr, isNode } from "java-slang/dist/ec-evaluator/utils";
+import { Group } from "react-konva";
+
+import { Visible } from "../../components/Visible";
+import { Config } from "../../CseMachineConfig";
+import { ControlStashConfig } from "../../CseMachineControlStashConfig";
+import { CseMachine } from "../CseMachine";
+import { ControlItem } from "./ControlItem";
+
+export class Control extends Visible {
+  private readonly _controlItems: ControlItem[] = [];
+
+  constructor(control: JavaControl) {
+    super();
+
+    // Position.
+    this._x = ControlStashConfig.ControlPosX;
+    this._y = ControlStashConfig.ControlPosY + ControlStashConfig.StashItemHeight + ControlStashConfig.StashItemTextPadding * 2;
+
+    // Create each ControlItem.
+    let controlItemY: number = this._y;
+    control.getStack().forEach((controlItem, index) => {
+      const controlItemText = this.getControlItemString(controlItem);
+      
+      const controlItemStroke =
+        index === control.getStack().length - 1 
+        ? Config.SA_CURRENT_ITEM
+        : ControlStashConfig.SA_WHITE;
+      
+      // TODO reference draw ltr?
+      const controlItemReference =
+        isInstr(controlItem) && controlItem.instrType === InstrType.ENV
+        ? CseMachine.environment?.frames.find(f => f.frame === (controlItem as EnvInstr).env)
+        : undefined;
+
+      const controlItemTooltip = this.getControlItemTooltip(controlItem);
+      this.getControlItemTooltip(controlItem);
+      
+      const node = isNode(controlItem) ? controlItem : controlItem.srcNode;
+      const highlightOnHover = () => {
+        let start = -1;
+        let end = -1;
+        if (node.location) {
+          start = node.location.startLine - 1;
+          end = node.location.endLine ? node.location.endLine - 1 : start;
+        }
+        CseMachine.setEditorHighlightedLines([[start, end]]);
+      };
+      const unhighlightOnHover = () => CseMachine.setEditorHighlightedLines([]);
+
+      const currControlItem = new ControlItem(
+        controlItemY,
+        controlItemText,
+        controlItemStroke,
+        controlItemReference,
+        controlItemTooltip,
+        highlightOnHover,
+        unhighlightOnHover,
+      );
+
+      this._controlItems.push(currControlItem);
+      controlItemY += currControlItem.height();
+    });
+
+    // Height and width.
+    this._height = controlItemY - this._y;
+    // TODO cal real width?
+    this._width = ControlStashConfig.ControlItemWidth;
+  }
+
+  draw(): React.ReactNode {
+    return (
+      <Group key={CseMachine.key++} ref={this.ref}>
+        {this._controlItems.map(c => c.draw())}
+      </Group>
+    );
+  }
+
+  private getControlItemString = (controlItem: JavaControlItem): string => {
+    if (isNode(controlItem)) {
+      return astToString(controlItem);
+    }
+
+    switch (controlItem.instrType) {
+      case InstrType.RESET:
+        return "return";
+      case InstrType.ASSIGNMENT:
+        return "asgn";
+      case InstrType.BINARY_OP:
+        const binOpInstr = controlItem as BinOpInstr;
+        return binOpInstr.symbol;
+      case InstrType.POP:
+        return "pop";
+      case InstrType.INVOCATION:
+        const appInstr = controlItem as InvInstr;
+        return `invoke ${appInstr.arity}`;
+      case InstrType.ENV:
+        return "env";
+      case InstrType.MARKER:
+        return "mark";
+      case InstrType.EVAL_VAR:
+        const evalVarInstr = controlItem as EvalVarInstr;
+        return `name ${evalVarInstr.symbol}`;
+      case InstrType.NEW:
+        const newInstr = controlItem as NewInstr;
+        return `new ${newInstr.c.frame.name}`;
+      case InstrType.RES_TYPE:
+        const resTypeInstr = controlItem as ResTypeInstr;
+        return `res_type ${resTypeInstr.value.kind === "Class" 
+          ? resTypeInstr.value.frame.name :
+          astToString(resTypeInstr.value)}`;
+      case InstrType.RES_TYPE_CONT:
+        const resTypeContInstr = controlItem as ResTypeContInstr;
+        return `res_type_cont ${resTypeContInstr.name}`;
+      case InstrType.RES_OVERLOAD:
+        const resOverloadInstr = controlItem as ResOverloadInstr;
+        return `res_overload ${resOverloadInstr.name} ${resOverloadInstr.arity}`;
+      case InstrType.RES_OVERRIDE:
+        return `res_override`;
+      case InstrType.RES_CON_OVERLOAD:
+        const resConOverloadInstr = controlItem as ResConOverloadInstr;
+        return `res_con_overload ${resConOverloadInstr.arity}`;
+      case InstrType.RES:
+        const resInstr = controlItem as ResInstr;
+        return `res ${resInstr.name}`;
+      case InstrType.DEREF:
+        return "deref";
+      default:
+        return "INSTRUCTION";
+    }
+  }
+
+  private getControlItemTooltip = (controlItem: JavaControlItem): string => {
+    if (isNode(controlItem)) {
+      return astToString(controlItem);
+    }
+
+    switch (controlItem.instrType) {
+      case InstrType.RESET:
+        return "Skip control items until marker instruction is reached";
+      case InstrType.ASSIGNMENT:
+        return "Assign value on top of stash to location on top of stash";
+      case InstrType.BINARY_OP:
+        const binOpInstr = controlItem as BinOpInstr;
+        return `Perform ${binOpInstr.symbol} on top 2 stash values`;
+      case InstrType.POP:
+        return "Pop most recently pushed value from stash";
+      case InstrType.INVOCATION:
+        const appInstr = controlItem as InvInstr;
+        return `Invoke method with ${appInstr.arity} argument${appInstr.arity === 1 ? '' : 's'}`;
+      case InstrType.ENV:
+        return "Set current environment to this environment";
+      case InstrType.MARKER:
+        return "Mark return address";
+      case InstrType.EVAL_VAR:
+        const evalVarInstr = controlItem as EvalVarInstr;
+        return `name ${evalVarInstr.symbol}`;
+      case InstrType.NEW:
+        const newInstr = controlItem as NewInstr;
+        return `Create new instance of class ${newInstr.c.frame.name}`;
+      case InstrType.RES_TYPE:
+        const resTypeInstr = controlItem as ResTypeInstr;
+        return `Resolve type of ${resTypeInstr.value.kind === "Class" 
+          ? resTypeInstr.value.frame.name :
+          astToString(resTypeInstr.value)}`;
+      case InstrType.RES_TYPE_CONT:
+        const resTypeContInstr = controlItem as ResTypeContInstr;
+        return `Resolve type of ${resTypeContInstr.name} in most recently pushed type from stash`;
+      case InstrType.RES_OVERLOAD:
+        const resOverloadInstr = controlItem as ResOverloadInstr;
+        return `Resolve overloading of method ${resOverloadInstr.name} with ${resOverloadInstr.arity} argument${resOverloadInstr.arity === 1 ? '' : 's'}`;
+      case InstrType.RES_OVERRIDE:
+        return "Resolve overriding of resolved method on top of stash";
+      case InstrType.RES_CON_OVERLOAD:
+        const resConOverloadInstr = controlItem as ResConOverloadInstr;
+        return `Resolve constructor overloading of class on stash with ${resConOverloadInstr.arity} argument${resConOverloadInstr.arity === 1 ? '' : 's'}`;
+      case InstrType.RES:
+        const resInstr = controlItem as ResInstr;
+        return `Resolve field ${resInstr.name} of most recently pushed value from stash`;
+      case InstrType.DEREF:
+        return "Dereference most recently pushed value from stash";
+      default:
+        return "INSTRUCTION";
+    }
+  }
+}
diff --git a/src/features/cseMachine/java/components/ControlItem.tsx b/src/features/cseMachine/java/components/ControlItem.tsx
new file mode 100644
index 0000000000..d50dba4d29
--- /dev/null
+++ b/src/features/cseMachine/java/components/ControlItem.tsx
@@ -0,0 +1,154 @@
+import { KonvaEventObject } from 'konva/lib/Node';
+import React, { RefObject } from 'react';
+import { Label, Tag, Text } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { IHoverable } from '../../CseMachineTypes';
+import {
+  getTextHeight,
+  setHoveredCursor,
+  setHoveredStyle,
+  setUnhoveredCursor,
+  setUnhoveredStyle,
+  truncateText,
+} from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+import { Arrow } from './Arrow';
+import { Frame } from './Frame';
+
+export class ControlItem extends Visible implements IHoverable {
+  private readonly _arrow: Arrow | undefined;
+  private readonly _tooltipRef: RefObject<any>;
+
+  constructor(
+    y: number,
+
+    private readonly _text: string,
+    private readonly _stroke: string,
+
+    reference: Frame | undefined,
+
+    private readonly _tooltip: string,
+    private readonly highlightOnHover: () => void,
+    private readonly unhighlightOnHover: () => void,
+  ) {
+    super();
+
+    // Position.
+    this._x = ControlStashConfig.ControlPosX;
+    this._y = y;
+    
+    // Text.
+    this._text = truncateText(
+      this._text,
+      ControlStashConfig.ControlMaxTextWidth,
+      ControlStashConfig.ControlMaxTextHeight
+    );
+
+    // Tooltip.
+    this._tooltipRef = React.createRef();
+
+    // Height and width.
+    this._height = getTextHeight(this._text, ControlStashConfig.ControlMaxTextWidth)
+      + ControlStashConfig.ControlItemTextPadding * 2;
+    this._width = ControlStashConfig.ControlItemWidth;
+
+    // Arrow
+    if (reference) {
+      this._arrow = new Arrow(
+        this._x + this._width,
+        this._y + this._height / 2,
+        reference.x(),
+        reference.y() + reference.height() / 2 + reference.name.height(),
+      );
+    }
+  }
+
+  private isCurrentItem = (): boolean => {
+    return this._stroke === Config.SA_CURRENT_ITEM;
+  }
+
+  onMouseEnter = (e: KonvaEventObject<MouseEvent>): void => {
+    this.highlightOnHover();
+    !this.isCurrentItem() && setHoveredStyle(e.currentTarget);
+    setHoveredCursor(e.currentTarget);
+    this._tooltipRef.current.show();
+  };
+
+  onMouseLeave = (e: KonvaEventObject<MouseEvent>): void => {
+    this.unhighlightOnHover();
+    !this.isCurrentItem() && setUnhoveredStyle(e.currentTarget);
+    setUnhoveredCursor(e.currentTarget);
+    this._tooltipRef.current.hide();
+  };
+
+  draw(): React.ReactNode {
+    const textProps = {
+      fill: ControlStashConfig.SA_WHITE,
+      padding: ControlStashConfig.ControlItemTextPadding,
+      fontFamily: ControlStashConfig.FontFamily,
+      fontSize: ControlStashConfig.FontSize,
+      fontStyle: ControlStashConfig.FontStyle,
+      fontVariant: ControlStashConfig.FontVariant,
+    };
+    const tagProps = {
+      stroke: this._stroke,
+      cornerRadius: ControlStashConfig.ControlItemCornerRadius,
+    };
+    return (
+      <React.Fragment key={CseMachine.key++}>
+        {/* Text */}
+        <Label
+          x={this.x()}
+          y={this.y()}
+          onMouseEnter={this.onMouseEnter}
+          onMouseLeave={this.onMouseLeave}
+          key={CseMachine.key++}
+        >
+          <Tag
+            {...ShapeDefaultProps}
+            {...tagProps}
+            key={CseMachine.key++}
+          />
+          <Text
+            {...ShapeDefaultProps}
+            {...textProps}
+            text={this._text}
+            width={this.width()}
+            height={this.height()}
+            key={CseMachine.key++}
+          />
+        </Label>
+
+        {/* Tooltip */}
+        <Label
+          x={this.x() + this.width() + ControlStashConfig.TooltipMargin}
+          y={this.y() + ControlStashConfig.TooltipMargin}
+          visible={false}
+          ref={this._tooltipRef}
+          key={CseMachine.key++}
+        >
+          <Tag
+            {...ShapeDefaultProps}
+            stroke="black"
+            fill={'black'}
+            opacity={ControlStashConfig.TooltipOpacity}
+            key={CseMachine.key++}
+          />
+          <Text
+            {...ShapeDefaultProps}
+            {...textProps}
+            text={this._tooltip}
+            padding={ControlStashConfig.TooltipPadding}
+            key={CseMachine.key++}
+          />
+        </Label>
+
+        {/* Arrow */}
+        {this._arrow?.draw()}
+      </React.Fragment>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Environment.tsx b/src/features/cseMachine/java/components/Environment.tsx
new file mode 100644
index 0000000000..0e456c170d
--- /dev/null
+++ b/src/features/cseMachine/java/components/Environment.tsx
@@ -0,0 +1,189 @@
+import { Environment as JavaEnvironment, EnvNode } from "java-slang/dist/ec-evaluator/components";
+import { Class as JavaClass, StructType } from "java-slang/dist/ec-evaluator/types";
+import { Group } from "react-konva";
+
+import { Visible } from "../../components/Visible";
+import { Config } from "../../CseMachineConfig";
+import { ControlStashConfig } from "../../CseMachineControlStashConfig";
+import { CseMachine } from "../CseMachine";
+import { Arrow } from "./Arrow";
+import { Frame } from "./Frame";
+import { Line } from "./Line";
+import { Object } from "./Object";
+import { Variable } from "./Variable";
+
+export class Environment extends Visible {
+  private readonly _methodFrames: Frame[] = [];
+  private readonly _objects: Object[] = [];
+  private readonly _classFrames: Frame[] = [];
+  private readonly _lines: Line[] = [];
+
+  constructor(environment: JavaEnvironment) {
+    super();
+
+    // Position.
+    this._x = ControlStashConfig.ControlPosX + ControlStashConfig.ControlItemWidth + 2 * Config.CanvasPaddingX;
+    this._y = ControlStashConfig.StashPosY + ControlStashConfig.StashItemHeight + 2 * Config.CanvasPaddingY;
+
+    // Create method frames.
+    const methodFramesX = this._x;
+    let methodFramesY: number = this._y;
+    let methodFramesWidth = Number(Config.FrameMinWidth);
+    environment.global.children.forEach(env => {
+      if (env.name.includes("(")) {
+        let currEnv: EnvNode | undefined = env;
+        let parentFrame;
+        while (currEnv) {
+          const stroke = currEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
+          const frame = new Frame(currEnv, methodFramesX, methodFramesY, stroke);
+          this._methodFrames.push(frame);
+          methodFramesY += (frame.height() + Config.FramePaddingY);
+          methodFramesWidth = Math.max(methodFramesWidth, frame.width());
+          parentFrame && frame.setParent(parentFrame);
+  
+          parentFrame = frame;
+          currEnv = currEnv.children.length ? currEnv.children[0] : undefined;
+        }
+      }
+    });
+
+    // Create objects.
+    const objectFramesX = methodFramesX + methodFramesWidth + Config.FrameMinWidth;
+    let objectFramesY: number = this._y;
+    let objectFramesWidth = Number(Config.FrameMinWidth);
+    environment.objects.forEach(obj => {
+      const objectFrames: Frame[] = [];
+      let objectFrameWidth = Number(Config.FrameMinWidth);
+
+      // Get top env.
+      let env: EnvNode | undefined = obj.frame;
+      while (env.parent) {
+        env = env.parent;
+      }
+
+      // Create frame top-down.
+      while (env) {
+        const stroke = env === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
+        const frame = new Frame(env, objectFramesX, objectFramesY, stroke);
+        // No padding btwn obj frames thus no arrows required.
+        objectFramesY += frame.height();
+        objectFramesWidth = Math.max(objectFramesWidth, frame.width());
+
+        env = env.children.length ? env.children[0] : undefined;
+
+        objectFrames.push(frame);
+        objectFrameWidth = Math.max(objectFrameWidth, frame.width());
+      }
+
+      // Standardize obj frames width.
+      objectFrames.forEach(o => o.setWidth(objectFrameWidth))
+
+      // Only add padding btwn objects.
+      objectFramesY += Config.FramePaddingY
+
+      this._objects.push(new Object(objectFrames, obj));
+    });
+
+    // Create class frames.
+    const classFramesX = objectFramesX + objectFramesWidth + Config.FrameMinWidth;
+    let classFramesY = this._y;
+    for (const [_, c] of environment.global.frame.entries()) {
+      const classEnv = (c as JavaClass).frame;
+      const classFrameStroke = classEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
+      const highlightOnHover = () => {
+        const node = (c as JavaClass).classDecl;
+        let start = -1;
+        let end = -1;
+        if (node.location) {
+          start = node.location.startLine - 1;
+          end = node.location.endLine ? node.location.endLine - 1 : start;
+        }
+        CseMachine.setEditorHighlightedLines([[start, end]]);
+      };
+      const unhighlightOnHover = () => CseMachine.setEditorHighlightedLines([]);
+      const classFrame = new Frame(classEnv, classFramesX, classFramesY, classFrameStroke, "", highlightOnHover, unhighlightOnHover);
+      const superClassName = (c as JavaClass).superclass?.frame.name;
+      if (superClassName) {
+        const parentFrame = this._classFrames.find(f => f.name.text === superClassName)!;
+        classFrame.setParent(parentFrame)
+      }
+      this._classFrames.push(classFrame);
+      classFramesY += (classFrame.height() + Config.FramePaddingY);
+    }
+
+    // Draw arrow for var ref in mtd frames to corresponding obj.
+    this._methodFrames.forEach(mf => {
+      mf.bindings.forEach(b => {
+        if (b.value instanceof Variable && b.value.variable.value.kind === StructType.OBJECT) {
+          const objFrame = b.value.variable.value.frame;
+          const matchingObj = this._objects.filter(o => o.getFrame().frame === objFrame)[0];
+          b.value.value = new Arrow(
+            b.value.x() + b.value.width() / 2,
+            b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
+            matchingObj.x(),
+            matchingObj.y() + matchingObj.height() / 2
+          );
+        }
+      })
+    });
+
+    // Draw arrow for var ref in obj frames to corresponding var or obj.
+    this._objects.flatMap(obj => obj.frames).forEach(of => {
+      of.bindings.forEach(b => {
+        if (b.value instanceof Variable && b.value.variable.value.kind === StructType.VARIABLE) {
+          const variable = b.value.variable.value;
+          const matchingVariable = this._classFrames.flatMap(c => c.bindings).filter(b => b.value instanceof Variable && b.value.variable === variable)[0].value as Variable;
+          b.value.value = new Arrow(
+            b.value.x() + b.value.width() / 2,
+            b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
+            matchingVariable.x(),
+            matchingVariable.y() + matchingVariable.type.height());
+        }
+        if (b.value instanceof Variable && b.value.variable.value.kind === StructType.OBJECT) {
+          const obj = b.value.variable.value.frame;
+          const matchingObj = this._objects.find(o => o.getFrame().frame === obj)!;
+          // Variable always has a box.
+          b.value.value = new Arrow(
+            b.value.x() + b.value.width() / 2,
+            b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
+            matchingObj.x(),
+            matchingObj.y() + matchingObj.height() / 2);
+        }
+      })
+    });
+
+    // Draw line for obj to class.
+    this._objects.forEach(obj => {
+      const matchingClass = this._classFrames.find(c => c.name.text === obj.object.class.frame.name)!;
+      const line = new Line(
+        obj.x() + obj.width(),
+        obj.y() + obj.height() / 2,
+        matchingClass.x(),
+        matchingClass.y() + matchingClass.height() / 2 + matchingClass.name.height());
+      this._lines.push(line);
+    });
+  }
+
+  get classes() {
+    return this._classFrames;
+  }
+
+  get objects() {
+    return this._objects.flatMap(obj => obj.frames);
+  }
+
+  get frames() {
+    return this._methodFrames;
+  }
+
+  draw(): React.ReactNode {
+    return (
+      <Group key={CseMachine.key++}>
+        {this._methodFrames.map(f => f.draw())}
+        {this._objects.flatMap(obj => obj.frames).map(f => f.draw())}
+        {this._classFrames.map(f => f.draw())}
+        {this._lines.map(f => f.draw())}
+      </Group>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Frame.tsx b/src/features/cseMachine/java/components/Frame.tsx
new file mode 100644
index 0000000000..975e07e79e
--- /dev/null
+++ b/src/features/cseMachine/java/components/Frame.tsx
@@ -0,0 +1,150 @@
+import { EnvNode } from 'java-slang/dist/ec-evaluator/components';
+import { KonvaEventObject } from 'konva/lib/Node';
+import React, { RefObject } from 'react';
+import { Group, Label, Rect, Tag, Text as KonvaText } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { IHoverable } from '../../CseMachineTypes';
+import { setHoveredCursor, setUnhoveredCursor } from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+import { Arrow } from './Arrow';
+import { Binding } from './Binding';
+import { Method } from './Method';
+import { Text } from './Text';
+
+export class Frame extends Visible implements IHoverable {
+  readonly tooltipRef: RefObject<any>;
+
+  readonly bindings: Binding[] = [];
+  readonly name: Text;
+  private parent: Frame | undefined;
+
+  constructor(
+    readonly frame: EnvNode,
+    x: number,
+    y: number,
+    readonly stroke: string,
+
+    readonly tooltip?: string,
+    readonly highlightOnHover?: () => void,
+    readonly unhighlightOnHover?: () => void,
+  ) {
+    super();
+
+    this._x = x;
+    this._y = y;
+
+    this.name = new Text(frame.name, this._x + Config.FramePaddingX, this._y);
+
+    this._width = Math.max(Config.FrameMinWidth, this.name.width() + 2 * Config.FramePaddingX);
+    this._height = Config.FramePaddingY + this.name.height();
+
+    // Create binding for each key-value pair
+    let bindingY: number = this._y + this.name.height() + Config.FramePaddingY;
+    for (const [key, data] of frame.frame) {
+      const currBinding: Binding = new Binding(key, data, this._x + Config.FramePaddingX, bindingY);
+      this.bindings.push(currBinding);
+      bindingY += (currBinding.height() + Config.FramePaddingY);
+      this._width = Math.max(this._width, currBinding.width() + 2 * Config.FramePaddingX);
+      this._height += (currBinding.height() + Config.FramePaddingY);
+    }
+
+    // Set x of Method aft knowing frame width.
+    this.bindings.filter(b => b.value instanceof Method).forEach(b => {
+      (b.value as Method).setX(this._x + this._width + Config.FramePaddingX);
+      b.setArrowToX(this._x + this._width + Config.FramePaddingX);
+    })
+
+    this.tooltipRef = React.createRef();
+  }
+
+  setWidth(width: number) {
+    this._width = width;
+  }
+
+  setParent(parent: Frame) {
+    this.parent = parent;
+  }
+
+  onMouseEnter = (e: KonvaEventObject<MouseEvent>) => {
+    this.highlightOnHover && this.highlightOnHover();
+    (this.tooltip || this.highlightOnHover) && setHoveredCursor(e.currentTarget);
+    this.tooltip && this.tooltipRef.current.show();
+  };
+
+  onMouseLeave = (e: KonvaEventObject<MouseEvent>) => {
+    this.unhighlightOnHover && this.unhighlightOnHover();
+    (this.tooltip || this.unhighlightOnHover) && setUnhoveredCursor(e.currentTarget);
+    this.tooltip && this.tooltipRef.current.hide();
+  };
+
+  draw(): React.ReactNode {
+    const textProps = {
+      fill: ControlStashConfig.SA_WHITE.toString(),
+      padding: Number(ControlStashConfig.ControlItemTextPadding),
+      fontFamily: ControlStashConfig.FontFamily.toString(),
+      fontSize: Number(ControlStashConfig.FontSize),
+      fontStyle: ControlStashConfig.FontStyle.toString(),
+      fontVariant: ControlStashConfig.FontVariant.toString()
+    };
+
+    return (
+      <Group key={CseMachine.key++}>
+        <Rect
+          {...ShapeDefaultProps}
+          x={this.x()}
+          y={this.y() + this.name.height()}
+          width={this.width()}
+          height={this.height()}
+          stroke={this.stroke}
+          cornerRadius={Number(Config.FrameCornerRadius)}
+          onMouseEnter={this.onMouseEnter}
+          onMouseLeave={this.onMouseLeave}
+          key={CseMachine.key++}
+        />
+        {/* Frame name */}
+        {this.name.draw()}
+
+        {/* Frame */}
+        {this.bindings.map(binding => binding.draw())}
+
+        {/* Frame parent */}
+        {this.parent && new Arrow(
+          this._x + Config.FramePaddingX / 2,
+          this._y + this.name.height(),
+          this.parent.x() + Config.FramePaddingX / 2,
+          // TODO WHY NEED TO ADD NAME HEIGHT?
+          this.parent.y() + this.parent.height() + this.name?.height()
+        ).draw()}
+        
+        {/* Frame tooltip */}
+        {this.tooltip && 
+          <Label
+            x={this.x() + this.width() + ControlStashConfig.TooltipMargin}
+            y={this.y() + ControlStashConfig.TooltipMargin}
+            visible={false}
+            ref={this.tooltipRef}
+            key={CseMachine.key++}
+          >
+            <Tag
+              {...ShapeDefaultProps}
+              stroke="black"
+              fill={'black'}
+              opacity={Number(ControlStashConfig.TooltipOpacity)}
+              key={CseMachine.key++}
+            />
+            <KonvaText
+              {...ShapeDefaultProps}
+              {...textProps}
+              text={this.tooltip}
+              padding={Number(ControlStashConfig.TooltipPadding)}
+              key={CseMachine.key++}
+            />
+          </Label>
+        }
+      </Group>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Line.tsx b/src/features/cseMachine/java/components/Line.tsx
new file mode 100644
index 0000000000..ec462a6846
--- /dev/null
+++ b/src/features/cseMachine/java/components/Line.tsx
@@ -0,0 +1,62 @@
+import { KonvaEventObject } from 'konva/lib/Node';
+import { Group, Path } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { IHoverable } from '../../CseMachineTypes';
+import {
+  setHoveredCursor,
+  setHoveredStyle,
+  setUnhoveredCursor,
+  setUnhoveredStyle,
+} from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+
+/** this class encapsulates a Line to be drawn between 2 points */
+export class Line extends Visible implements IHoverable {
+  private static readonly TO_X_INDEX = 2;
+  private readonly _points: number[] = [];
+
+  constructor(
+    fromX: number,
+    fromY: number,
+    toX: number,
+    toY: number,
+  ) {
+    super();
+    this._points.push(fromX, fromY, toX, toY);
+  }
+
+  setToX(x: number) {
+    this._points[Line.TO_X_INDEX] = x;
+  }
+
+  onMouseEnter(e: KonvaEventObject<MouseEvent>) {
+    setHoveredStyle(e.currentTarget)
+    setHoveredCursor(e.currentTarget);
+  }
+
+  onMouseLeave(e: KonvaEventObject<MouseEvent>) {
+    setUnhoveredStyle(e.currentTarget)
+    setUnhoveredCursor(e.currentTarget);
+  }
+
+  draw() {
+    const path = `M ${this._points[0]} ${this._points[1]} L ${this._points[2]} ${this._points[3]}`;
+    return (
+      <Group key={CseMachine.key++}
+        onMouseEnter={e => this.onMouseEnter(e)}
+        onMouseLeave={e => this.onMouseLeave(e)}
+      >
+        <Path
+          {...ShapeDefaultProps}
+          stroke={String(Config.SA_WHITE)}
+          strokeWidth={Number(Config.ArrowStrokeWidth)}
+          hitStrokeWidth={Number(Config.ArrowHitStrokeWidth)}
+          data={path}
+          key={CseMachine.key++}
+        />
+      </Group>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Method.tsx b/src/features/cseMachine/java/components/Method.tsx
new file mode 100644
index 0000000000..f871358444
--- /dev/null
+++ b/src/features/cseMachine/java/components/Method.tsx
@@ -0,0 +1,119 @@
+import { astToString } from 'java-slang/dist/ast/utils/astToString';
+import { Closure as JavaMethod } from 'java-slang/dist/ec-evaluator/types';
+import { KonvaEventObject } from 'konva/lib/Node';
+import React, { RefObject } from 'react';
+import { Circle, Group, Label, Tag, Text } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { IHoverable } from '../../CseMachineTypes';
+import {
+  getTextHeight,
+  getTextWidth,
+  setHoveredCursor,
+  setUnhoveredCursor,
+} from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+
+export class Method extends Visible implements IHoverable {
+  private _centerX: number;
+  
+  private readonly _tooltipRef: RefObject<any>;
+  private readonly _tooltip: string;
+
+  constructor(
+    x: number,
+    y: number,
+    private readonly _method: JavaMethod,
+  ) {
+    super();
+
+    // Position.
+    this._x = x;
+    this._y = y;
+
+    // Circle.
+    this._centerX = this._x + Config.FnRadius * 2;
+
+    // Tooltip.
+    this._tooltipRef = React.createRef();
+    this._tooltip = astToString(this._method.mtdOrCon);
+  }
+
+  get method() {
+    return this._method;
+  }
+
+  setX(x: number) {
+    this._x = x;
+    this._centerX = this._x + Config.FnRadius * 2;
+  }
+
+  onMouseEnter = (e: KonvaEventObject<MouseEvent>) => {
+    setHoveredCursor(e.currentTarget);
+    this._tooltipRef.current.show();
+  };
+
+  onMouseLeave = (e: KonvaEventObject<MouseEvent>) => {
+    setUnhoveredCursor(e.currentTarget);
+    this._tooltipRef.current.hide();
+  };
+
+  draw(): React.ReactNode {
+    return (
+      <React.Fragment key={CseMachine.key++}>
+        <Group
+          onMouseEnter={e => this.onMouseEnter(e)}
+          onMouseLeave={e => this.onMouseLeave(e)}
+          // ref={this.ref}
+          key={CseMachine.key++}
+        >
+          {/* Left outer */}
+          <Circle
+            {...ShapeDefaultProps}
+            key={CseMachine.key++}
+            x={this._centerX - Config.FnRadius}
+            y={this.y()}
+            radius={Config.FnRadius}
+            stroke={ControlStashConfig.SA_WHITE}
+          />
+          {/* Left inner */}
+          <Circle
+            {...ShapeDefaultProps}
+            key={CseMachine.key++}
+            x={this._centerX - Config.FnRadius}
+            y={this.y()}
+            radius={Config.FnInnerRadius}
+            fill={String(ControlStashConfig.SA_WHITE)}
+          />
+        </Group>
+
+        {/* Tooltip */}
+        <Label
+          x={this.x() + Config.FnRadius * 2 + Config.TextPaddingX * 2}
+          y={this.y() - getTextHeight(this._tooltip, getTextWidth(this._tooltip)) / 2}
+          visible={false}
+          ref={this._tooltipRef}
+          key={CseMachine.key++}
+        >
+          <Tag
+            stroke="black"
+            fill={'black'}
+            opacity={Config.FnTooltipOpacity}
+            key={CseMachine.key++}
+          />
+          <Text
+            text={this._tooltip}
+            fontFamily={ControlStashConfig.FontFamily}
+            fontSize={ControlStashConfig.FontSize}
+            fontStyle={ControlStashConfig.FontStyle}
+            fill={ControlStashConfig.SA_WHITE}
+            padding={5}
+            key={CseMachine.key++}
+          />
+        </Label>
+      </React.Fragment>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Object.tsx b/src/features/cseMachine/java/components/Object.tsx
new file mode 100644
index 0000000000..054a72944a
--- /dev/null
+++ b/src/features/cseMachine/java/components/Object.tsx
@@ -0,0 +1,44 @@
+import { Object as JavaObject } from 'java-slang/dist/ec-evaluator/types';
+import React from 'react';
+import { Group } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { CseMachine } from '../CseMachine';
+import { Frame } from './Frame';
+
+export class Object extends Visible {
+  constructor(
+    private readonly _frames: Frame[],
+    private readonly _object: JavaObject,
+  ) {
+    super();
+
+    // Position.
+    this._x = _frames[0].x();
+    this._y = _frames[0].y();
+
+    // Height and width.
+    this._height = this._frames.reduce((accHeight, currFrame) => accHeight + currFrame.height(), 0);
+    this._width = this._frames.reduce((maxWidth, currFrame) => Math.max(maxWidth, currFrame.width()), 0);
+  }
+
+  get frames() {
+    return this._frames;
+  }
+
+  get object() {
+    return this._object;
+  }
+
+  getFrame(): Frame {
+    return this._frames[this._frames.length - 1];
+  }
+
+  draw(): React.ReactNode {
+    return (
+      <Group key={CseMachine.key++}>
+        {this._frames.map(f => f.draw())}
+      </Group>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Stash.tsx b/src/features/cseMachine/java/components/Stash.tsx
new file mode 100644
index 0000000000..d303dc6fe0
--- /dev/null
+++ b/src/features/cseMachine/java/components/Stash.tsx
@@ -0,0 +1,90 @@
+import { Stash as JavaStash } from 'java-slang/dist/ec-evaluator/components';
+import { StashItem as JavaStashItem, StructType } from 'java-slang/dist/ec-evaluator/types';
+import React from 'react';
+import { Group } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { CseMachine } from '../CseMachine';
+import { Method } from './Method';
+import { StashItem } from './StashItem';
+import { Variable } from './Variable';
+
+export class Stash extends Visible {
+  private readonly _stashItems: StashItem[] = [];
+
+  constructor(stash: JavaStash) {
+    super();
+
+    // Position.
+    this._x = ControlStashConfig.StashPosX;
+    this._y = ControlStashConfig.StashPosY;
+
+    // Create each StashItem.
+    let stashItemX: number = this._x;
+    for (const stashItem of stash.getStack()) {
+      const stashItemText = this.getStashItemString(stashItem);
+      const stashItemStroke = ControlStashConfig.SA_WHITE;
+      const stashItemReference = this.getStashItemRef(stashItem);
+      const currStashItem = new StashItem(
+        stashItemX, 
+        stashItemText,
+        stashItemStroke,
+        stashItemReference);
+
+      this._stashItems.push(currStashItem);
+      stashItemX += currStashItem.width();
+    }
+
+    // Height and width.
+    this._height = ControlStashConfig.StashItemHeight;
+    this._width = stashItemX - this._x;
+  }
+
+  draw(): React.ReactNode {
+    return (
+      <Group key={CseMachine.key++} >
+        {this._stashItems.map(s => s.draw())}
+      </Group>
+    );
+  }
+
+  private getStashItemString = (stashItem: JavaStashItem): string => {
+    switch (stashItem.kind) {
+      case "Literal":
+        return stashItem.literalType.value;
+      case StructType.VARIABLE:
+        return "location";
+      case StructType.TYPE:
+        return stashItem.type;
+      default:
+        return stashItem.kind.toLowerCase();
+    }
+  }
+
+  private getStashItemRef = (stashItem: JavaStashItem) => {
+    return stashItem.kind === StructType.CLOSURE
+      ? CseMachine.environment &&
+        CseMachine.environment.classes
+          .flatMap(c => c.bindings)
+          .find(b => b.value instanceof Method && b.value.method === stashItem)?.value as Method
+      : stashItem.kind === StructType.VARIABLE
+      ? CseMachine.environment && 
+        (CseMachine.environment.frames
+          .flatMap(c => c.bindings)
+          .find(b => b.value instanceof Variable && b.value.variable === stashItem)?.value as Variable ||
+        CseMachine.environment.classes
+          .flatMap(c => c.bindings)
+          .find(b => b.value instanceof Variable && b.value.variable === stashItem)?.value as Variable ||
+        CseMachine.environment.objects
+          .flatMap(o => o.bindings)
+          .find(b => b.value instanceof Variable && b.value.variable === stashItem)?.value as Variable)
+      : stashItem.kind === StructType.CLASS
+      ? CseMachine.environment &&
+        CseMachine.environment.classes.find(c => c.frame === stashItem.frame)
+      : stashItem.kind === StructType.OBJECT
+      ? CseMachine.environment &&
+        CseMachine.environment.objects.find(o => o.frame === stashItem.frame)
+      : undefined;
+  }
+}
diff --git a/src/features/cseMachine/java/components/StashItem.tsx b/src/features/cseMachine/java/components/StashItem.tsx
new file mode 100644
index 0000000000..2cdb91d8f1
--- /dev/null
+++ b/src/features/cseMachine/java/components/StashItem.tsx
@@ -0,0 +1,95 @@
+import React from 'react';
+import {
+  Group as KonvaGroup,
+  Label as KonvaLabel,
+  Tag as KonvaTag,
+  Text as KonvaText,
+} from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { ShapeDefaultProps } from '../../CseMachineConfig';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { getTextWidth } from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+import { Arrow } from './Arrow';
+import { Frame } from './Frame';
+import { Method } from './Method';
+import { Variable } from './Variable';
+
+export class StashItem extends Visible {
+  private readonly _arrow: Arrow | undefined;
+
+  constructor(
+    x: number,
+    private readonly _text: string,
+    private readonly _stroke: string,
+    reference?: Method | Frame | Variable,
+  ) {
+    super();
+
+    // Position.
+    this._x = x;
+    this._y = ControlStashConfig.StashPosY;
+    
+    // Height and width.
+    this._height = ControlStashConfig.StashItemHeight + ControlStashConfig.StashItemTextPadding * 2;
+    this._width = ControlStashConfig.StashItemTextPadding * 2 + getTextWidth(this._text);
+
+    // Arrow
+    if (reference) {
+      const toY =
+        reference instanceof Frame
+        ? reference.y() + reference.name.height()
+        : reference instanceof Method
+        ? reference.y()
+        : reference.y() + reference.type.height();
+      this._arrow = new Arrow(
+        this._x + this._width / 2,
+        this._y + this._height,
+        reference.x(),
+        toY);
+    }
+  }
+
+  draw(): React.ReactNode {
+    const textProps = {
+      fill: ControlStashConfig.SA_WHITE,
+      padding: ControlStashConfig.StashItemTextPadding,
+      fontFamily: ControlStashConfig.FontFamily,
+      fontSize: ControlStashConfig.FontSize,
+      fontStyle: ControlStashConfig.FontStyle,
+      fontVariant: ControlStashConfig.FontVariant,
+    };
+
+    const tagProps = {
+      stroke: this._stroke,
+      cornerRadius: ControlStashConfig.StashItemCornerRadius,
+    };
+
+    return (
+      <KonvaGroup key={CseMachine.key++}>
+        {/* Text */}
+        <KonvaLabel
+          x={this.x()}
+          y={this.y()}
+          key={CseMachine.key++}
+        >
+          <KonvaTag
+            {...ShapeDefaultProps}
+            {...tagProps}
+            key={CseMachine.key++}
+          />
+          <KonvaText
+            {...ShapeDefaultProps}
+            {...textProps}
+            text={this._text}
+            key={CseMachine.key++}
+          />
+        </KonvaLabel>
+
+        {/* Arrow */}
+        {this._arrow?.draw()}
+      </KonvaGroup>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Text.tsx b/src/features/cseMachine/java/components/Text.tsx
new file mode 100644
index 0000000000..89d10c3366
--- /dev/null
+++ b/src/features/cseMachine/java/components/Text.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { Group as KonvaGroup, Label as KonvaLabel, Text as KonvaText } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { getTextWidth } from '../../CseMachineUtils';
+import { CseMachine } from '../CseMachine';
+
+/** this class encapsulates a string to be drawn onto the canvas */
+export class Text extends Visible {
+  constructor(
+    private readonly _text: string,
+    x: number,
+    y: number,
+  ) {
+    super();
+
+    // Position
+    this._x = x;
+    this._y = y;
+
+    // Height and width
+    this._height = Config.FontSize;
+    this._width = getTextWidth(this._text);
+  }
+
+  get text() {
+    return this._text;
+  }
+
+  setY(y: number) {
+    this._y = y;
+  }
+
+  draw(): React.ReactNode {
+    const props = {
+      fontFamily: Config.FontFamily,
+      fontSize: Config.FontSize,
+      fontStyle: Config.FontStyle,
+      fill: Config.SA_WHITE,
+    };
+
+    return (
+      <KonvaGroup key={CseMachine.key++}>
+        <KonvaLabel
+          x={this.x()}
+          y={this.y()}
+          key={CseMachine.key++}
+        >
+          <KonvaText
+            {...ShapeDefaultProps} 
+            key={CseMachine.key++} 
+            text={this._text}
+            {...props}
+          />
+        </KonvaLabel>
+      </KonvaGroup>
+    );
+  }
+}
diff --git a/src/features/cseMachine/java/components/Variable.tsx b/src/features/cseMachine/java/components/Variable.tsx
new file mode 100644
index 0000000000..a784db2f7e
--- /dev/null
+++ b/src/features/cseMachine/java/components/Variable.tsx
@@ -0,0 +1,109 @@
+import { StructType, Variable as JavaVariable } from 'java-slang/dist/ec-evaluator/types';
+import React from 'react';
+import { Group, Rect } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config, ShapeDefaultProps } from '../../CseMachineConfig';
+import { CseMachine } from '../CseMachine';
+import { Arrow } from './Arrow';
+import { Text } from './Text';
+
+export interface TextOptions {
+  maxWidth: number;
+  fontSize: number;
+  fontFamily: string;
+  fontStyle: string;
+  fontVariant: string;
+  isStringIdentifiable: boolean;
+}
+
+export const defaultOptions: TextOptions = {
+  maxWidth: Number.MAX_VALUE, // maximum width this text should be
+  fontFamily: Config.FontFamily.toString(), // default is Arial
+  fontSize: Number(Config.FontSize), // in pixels. Default is 12
+  fontStyle: Config.FontStyle.toString(), // can be normal, bold, or italic. Default is normal
+  fontVariant: Config.FontVariant.toString(), // can be normal or small-caps. Default is normal
+  isStringIdentifiable: false // if true, contain strings within double quotation marks "". Default is false
+};
+
+export class Variable extends Visible {
+  private readonly _type: Text;
+  private _value: Text | Arrow;
+
+  constructor(
+    x: number,
+    y: number,
+    private readonly _variable: JavaVariable,
+  ) {
+    super();
+
+    // Position.
+    this._x = x;
+    this._y = y;
+
+    // Type.
+    this._type = new Text(
+      this._variable.type,
+      this._x,
+      this._y);
+
+    // Value.
+    if (this.variable.value.kind === "Literal") {
+      this._value = new Text(
+        this.variable.value.literalType.value,
+        this._x + Config.TextPaddingX,
+        this._y + this._type.height() + Config.TextPaddingX);
+    } else if (this.variable.value.kind === StructType.SYMBOL) {
+      this._value = new Text(
+        "",
+        this._x + Config.TextPaddingX,
+        this._y + this._type.height()  + Config.TextPaddingX);
+    } else {
+      this._value = new Text(
+        "",
+        this._x + Config.TextPaddingX,
+        this._y + this._type.height()  + Config.TextPaddingX);
+    }
+    
+    // Height and width.
+    this._height = this._type.height() + this._value.height() + 2 * Config.TextPaddingX;
+    this._width = Math.max(this._type.width(), this._value.width() + 2 * Config.TextPaddingX);
+  }
+
+  get variable() {
+    return this._variable;
+  }
+
+  set value(value: Arrow) {
+    this._value = value;
+    this._height = this._type.height() + Config.FontSize + 2 * Config.TextPaddingX;
+    this._width = Math.max(this._type.width(), Config.TextMinWidth);
+  }
+
+  get type() {
+    return this._type;
+  }
+
+  draw(): React.ReactNode {
+    return (
+      <Group key={CseMachine.key++}>
+        {/* Type */}
+        {this._type.draw()}
+
+        {/* Box */}
+        <Rect
+          {...ShapeDefaultProps}
+          x={this._x}
+          y={this._y + this._type.height()}
+          width={this._width}
+          height={this._height - this._type.height()}
+          stroke={Config.SA_WHITE}
+          key={CseMachine.key++}
+        />
+
+        {/* Text */}
+        {this._value.draw()}
+      </Group>
+    );
+  }
+}

From b384d101b7f30fccdf179d6cfa40af91a2c26da3 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Wed, 10 Apr 2024 22:55:27 +0800
Subject: [PATCH 03/12] Integrate CSEC Visualizer

---
 src/commons/application/ApplicationTypes.ts   |   2 +-
 src/commons/sagas/PlaygroundSaga.ts           |   6 +
 .../sagas/WorkspaceSaga/helpers/evalCode.ts   |   5 +-
 .../WorkspaceSaga/helpers/updateInspector.ts  |  39 ++++--
 .../content/SideContentCseMachine.tsx         | 123 ++++++++++++------
 src/commons/utils/JavaHelper.ts               |  53 +++++++-
 6 files changed, 177 insertions(+), 51 deletions(-)

diff --git a/src/commons/application/ApplicationTypes.ts b/src/commons/application/ApplicationTypes.ts
index 2015c1007c..0743db122c 100644
--- a/src/commons/application/ApplicationTypes.ts
+++ b/src/commons/application/ApplicationTypes.ts
@@ -219,7 +219,7 @@ export const javaLanguages: SALanguage[] = [
     variant: Variant.DEFAULT,
     displayName: 'Java',
     mainLanguage: SupportedLanguage.JAVA,
-    supports: {}
+    supports: { cseMachine: true }
   }
 ];
 export const cLanguages: SALanguage[] = [
diff --git a/src/commons/sagas/PlaygroundSaga.ts b/src/commons/sagas/PlaygroundSaga.ts
index f8e1409145..e7acf4fe01 100644
--- a/src/commons/sagas/PlaygroundSaga.ts
+++ b/src/commons/sagas/PlaygroundSaga.ts
@@ -5,6 +5,7 @@ import qs from 'query-string';
 import { SagaIterator } from 'redux-saga';
 import { call, delay, put, race, select } from 'redux-saga/effects';
 import CseMachine from 'src/features/cseMachine/CseMachine';
+import { CseMachine as JavaCseMachine } from 'src/features/cseMachine/java/CseMachine';
 
 import {
   changeQueryString,
@@ -100,12 +101,17 @@ export default function* PlaygroundSaga(): SagaIterator {
       if (newId !== SideContentType.cseMachine) {
         yield put(toggleUsingCse(false, workspaceLocation));
         yield call([CseMachine, CseMachine.clearCse]);
+        yield call([JavaCseMachine, JavaCseMachine.clearCse]);
         yield put(updateCurrentStep(-1, workspaceLocation));
         yield put(updateStepsTotal(0, workspaceLocation));
         yield put(toggleUpdateCse(true, workspaceLocation));
         yield put(setEditorHighlightedLines(workspaceLocation, 0, []));
       }
 
+      if (playgroundSourceChapter === Chapter.FULL_JAVA && newId === SideContentType.cseMachine) {
+        yield put(toggleUsingCse(true, workspaceLocation));
+      }
+
       if (
         isSourceLanguage(playgroundSourceChapter) &&
         (newId === SideContentType.substVisualizer || newId === SideContentType.cseMachine)
diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts
index 6dc00046f8..3f4f6e208c 100644
--- a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts
+++ b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts
@@ -250,6 +250,9 @@ export function* evalCode(
   let lastDebuggerResult = yield select(
     (state: OverallState) => state.workspaces[workspaceLocation].lastDebuggerResult
   );
+  const isUsingCse = yield select(
+    (state: OverallState) => state.workspaces["playground"].usingCse
+  );
 
   // Handles `console.log` statements in fullJS
   const detachConsole: () => void =
@@ -266,7 +269,7 @@ export function* evalCode(
         : isC
         ? call(cCompileAndRun, entrypointCode, context)
         : isJava
-        ? call(javaRun, entrypointCode, context)
+        ? call(javaRun, entrypointCode, context, currentStep, isUsingCse)
         : call(
             runFilesInContext,
             isFolderModeEnabled
diff --git a/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts b/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts
index 7a0305091b..f109aa76e9 100644
--- a/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts
+++ b/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts
@@ -1,24 +1,41 @@
+import { Chapter } from 'js-slang/dist/types';
 import { SagaIterator } from 'redux-saga';
 import { put, select } from 'redux-saga/effects';
 
 import { OverallState } from '../../../application/ApplicationTypes';
 import { actions } from '../../../utils/ActionsHelper';
+import { visualizeJavaCseMachine } from '../../../utils/JavaHelper';
 import { visualizeCseMachine } from '../../../utils/JsSlangHelper';
 import { WorkspaceLocation } from '../../../workspace/WorkspaceTypes';
 
 export function* updateInspector(workspaceLocation: WorkspaceLocation): SagaIterator {
   try {
-    const lastDebuggerResult = yield select(
-      (state: OverallState) => state.workspaces[workspaceLocation].lastDebuggerResult
-    );
-    const row = lastDebuggerResult.context.runtime.nodes[0].loc.start.line - 1;
-    // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added.
-    yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, []));
-    // We highlight only one row to show the current line
-    // If we highlight from start to end, the whole program block will be highlighted at the start
-    // since the first node is the program node
-    yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, [[row, row]]));
-    visualizeCseMachine(lastDebuggerResult);
+    const [lastDebuggerResult, chapter] = yield select((state: OverallState) => [
+      state.workspaces[workspaceLocation].lastDebuggerResult,
+      state.workspaces[workspaceLocation].context.chapter,
+    ]);
+    if (chapter === Chapter.FULL_JAVA) {
+      const controlItem = lastDebuggerResult.context.control.peek();
+      let start = -1;
+      let end = -1;
+      if (controlItem?.srcNode?.location) {
+        const node = controlItem.srcNode;
+        start = node.location.startLine - 1;
+        end = node.location.endLine ? node.location.endLine - 1 : start;
+      }
+      yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, []));
+      yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, [[start, end]]));
+      visualizeJavaCseMachine(lastDebuggerResult);
+    } else {
+      const row = lastDebuggerResult.context.runtime.nodes[0].loc.start.line - 1;
+      // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added.
+      yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, []));
+      // We highlight only one row to show the current line
+      // If we highlight from start to end, the whole program block will be highlighted at the start
+      // since the first node is the program node
+      yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, [[row, row]]));
+      visualizeCseMachine(lastDebuggerResult);
+    }
   } catch (e) {
     // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added.
     yield put(actions.setEditorHighlightedLines(workspaceLocation, 0, []));
diff --git a/src/commons/sideContent/content/SideContentCseMachine.tsx b/src/commons/sideContent/content/SideContentCseMachine.tsx
index 009c2aec85..e77c96a220 100644
--- a/src/commons/sideContent/content/SideContentCseMachine.tsx
+++ b/src/commons/sideContent/content/SideContentCseMachine.tsx
@@ -10,6 +10,7 @@ import {
 import { IconNames } from '@blueprintjs/icons';
 import { Tooltip2 } from '@blueprintjs/popover2';
 import classNames from 'classnames';
+import { Chapter } from 'js-slang/dist/types';
 import { debounce } from 'lodash';
 import React from 'react';
 import { HotKeys } from 'react-hotkeys';
@@ -20,6 +21,7 @@ import type { PlaygroundWorkspaceState } from 'src/commons/workspace/WorkspaceTy
 import CseMachine from 'src/features/cseMachine/CseMachine';
 import { CseAnimation } from 'src/features/cseMachine/CseMachineAnimation';
 import { Layout } from 'src/features/cseMachine/CseMachineLayout';
+import { CseMachine as JavaCseMachine } from 'src/features/cseMachine/java/CseMachine';
 
 import { InterpreterOutput, OverallState } from '../../application/ApplicationTypes';
 import { HighlightedLines } from '../../editor/EditorTypes';
@@ -40,6 +42,7 @@ type State = {
   width: number;
   lastStep: boolean;
   stepLimitExceeded: boolean;
+  chapter: Chapter;
 };
 
 type CseMachineProps = OwnProps & StateProps & DispatchProps;
@@ -53,6 +56,7 @@ type StateProps = {
   changepointSteps: number[];
   needCseUpdate: boolean;
   machineOutput: InterpreterOutput[];
+  chapter: Chapter;
 };
 
 type OwnProps = {
@@ -85,25 +89,39 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
       width: this.calculateWidth(props.editorWidth),
       height: this.calculateHeight(props.sideContentHeight),
       lastStep: false,
-      stepLimitExceeded: false
+      stepLimitExceeded: false,
+      chapter: props.chapter,
     };
-    CseMachine.init(
-      visualization => {
-        this.setState({ visualization }, () => CseAnimation.playAnimation());
-        if (visualization) this.props.handleAlertSideContent();
-      },
-      this.state.width,
-      this.state.height,
-      (segments: [number, number][]) => {
-        // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added.
-        // This comment is copied over from workspace saga
-        props.setEditorHighlightedLines(0, segments);
-      },
-      // We shouldn't be able to move slider to a step number beyond the step limit
-      isControlEmpty => {
-        this.setState({ stepLimitExceeded: false });
-      }
-    );
+    if (this.isJava()) {
+      JavaCseMachine.init(
+        visualization => this.setState({ visualization }),
+        (segments: [number, number][]) => {
+          props.setEditorHighlightedLines(0, segments);
+        }
+      );
+    } else {
+      CseMachine.init(
+        visualization => {
+          this.setState({ visualization }, () => CseAnimation.playAnimation());
+          if (visualization) this.props.handleAlertSideContent();
+        },
+        this.state.width,
+        this.state.height,
+        (segments: [number, number][]) => {
+          // TODO: Hardcoded to make use of the first editor tab. Rewrite after editor tabs are added.
+          // This comment is copied over from workspace saga
+          props.setEditorHighlightedLines(0, segments);
+        },
+        // We shouldn't be able to move slider to a step number beyond the step limit
+        isControlEmpty => {
+          this.setState({ stepLimitExceeded: false });
+        }
+      );
+    }
+  }
+
+  private isJava(): boolean {
+    return this.props.chapter === Chapter.FULL_JAVA;
   }
 
   private calculateWidth(editorWidth?: string) {
@@ -173,7 +191,11 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
     }
     if (prevProps.needCseUpdate && !this.props.needCseUpdate) {
       this.stepFirst();
-      CseMachine.clearCse();
+      if (this.isJava()) {
+        JavaCseMachine.clearCse();
+      } else {
+        CseMachine.clearCse();
+      }
     }
   }
 
@@ -210,8 +232,8 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
             onRelease={this.sliderRelease}
             value={this.state.value < 0 ? 0 : this.state.value}
           />
-          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
-            <ButtonGroup>
+          <div style={{ display: 'flex', justifyContent: this.isJava() ? 'center' : 'space-between', alignItems: 'center' }}>
+            {!this.isJava() && <ButtonGroup>
               <Tooltip2 content="Control and Stash" compact>
                 <AnchorButton
                   onMouseUp={() => {
@@ -248,7 +270,7 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
                   />
                 </AnchorButton>
               </Tooltip2>
-            </ButtonGroup>
+            </ButtonGroup>}
             <ButtonGroup>
               <Button
                 disabled={!this.state.visualization}
@@ -259,13 +281,13 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
                 disabled={!this.state.visualization}
                 icon="chevron-left"
                 onClick={
-                  CseMachine.getControlStash() ? this.stepPrevious : this.stepPrevChangepoint
+                  this.isJava() || CseMachine.getControlStash() ? this.stepPrevious : this.stepPrevChangepoint
                 }
               />
               <Button
                 disabled={!this.state.visualization}
                 icon="chevron-right"
-                onClick={CseMachine.getControlStash() ? this.stepNext : this.stepNextChangepoint}
+                onClick={this.isJava() || CseMachine.getControlStash() ? this.stepNext : this.stepNextChangepoint}
               />
               <Button
                 disabled={!this.state.visualization}
@@ -273,7 +295,7 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
                 onClick={this.stepNextBreakpoint}
               />
             </ButtonGroup>
-            <ButtonGroup>
+            {!this.isJava() && <ButtonGroup>
               <Tooltip2 content="Print" compact>
                 <AnchorButton
                   onMouseUp={() => {
@@ -299,7 +321,7 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
                   onClick={Layout.exportImage}
                 />
               </Tooltip2>
-            </ButtonGroup>
+            </ButtonGroup>}
           </div>
         </div>{' '}
         {this.state.visualization &&
@@ -331,14 +353,32 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
             className={Classes.RUNNING_TEXT}
             data-testid="cse-machine-default-text"
           >
-            The CSE machine generates control, stash and environment model diagrams following a
-            notation introduced in{' '}
-            <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
-              <i>
-                Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3,
-                Section 2
-              </i>
-            </a>
+            {this.isJava()
+            ? <span>
+              The CSEC machine generates control, stash, environment and class model diagrams adapted from the
+              notation introduced in{' '}
+              <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
+                <i>
+                  Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3,
+                  Section 2
+                </i>
+              </a>{'. '}
+
+              You have chosen the sublanguage{' '}
+              <a href={`${Links.sourceDocs}java_csec/`} rel="noopener noreferrer" target="_blank">
+                <i>Java CSEC</i>
+              </a>
+            </span>
+            : <span>
+              The CSE machine generates control, stash and environment model diagrams following a
+              notation introduced in{' '}
+              <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
+                <i>
+                  Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3,
+                  Section 2
+                </i>
+              </a>
+            </span>}
             .
             <br />
             <br /> On this tab, the REPL will be hidden from view, so do check that your code has no
@@ -371,13 +411,13 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
           <Button
             icon="plus"
             disabled={!this.state.visualization}
-            onClick={() => Layout.zoomStage(true, 5)}
+            onClick={() => this.zoomStage(true, 5)}
             style={{ marginBottom: '5px', borderRadius: '3px' }}
           />
           <Button
             icon="minus"
             disabled={!this.state.visualization}
-            onClick={() => Layout.zoomStage(false, 5)}
+            onClick={() => this.zoomStage(false, 5)}
             style={{ borderRadius: '3px' }}
           />
         </ButtonGroup>
@@ -385,6 +425,14 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
     );
   }
 
+  private zoomStage = (isZoomIn: boolean, multiplier: number) => {
+    if (this.isJava()) {
+      JavaCseMachine.zoomStage(isZoomIn, multiplier);
+    } else {
+      Layout.zoomStage(false, 5);
+    }
+  }
+
   private sliderRelease = (newValue: number) => {
     if (newValue === this.props.stepsTotal) {
       this.setState({ lastStep: true });
@@ -509,7 +557,8 @@ const mapStateToProps: MapStateToProps<StateProps, OwnProps, OverallState> = (
     breakpointSteps: workspace.breakpointSteps,
     changepointSteps: workspace.changepointSteps,
     needCseUpdate: workspace.updateCse,
-    machineOutput: workspace.output
+    machineOutput: workspace.output,
+    chapter: workspace.context.chapter,
   };
 };
 
diff --git a/src/commons/utils/JavaHelper.ts b/src/commons/utils/JavaHelper.ts
index 21ddc66f51..0d4d0ade5f 100644
--- a/src/commons/utils/JavaHelper.ts
+++ b/src/commons/utils/JavaHelper.ts
@@ -1,12 +1,17 @@
+import { SourceError as JavaSourceError } from 'java-slang/dist/ec-evaluator/errors';
+import { runECEvaluator } from 'java-slang/dist/ec-evaluator/index';
+import { Context as JavaContext } from 'java-slang/dist/ec-evaluator/types';
 import setupJVM, { parseBin } from 'java-slang/dist/jvm';
 import { createModuleProxy, loadCachedFiles } from 'java-slang/dist/jvm/utils/integration';
 import { Context } from 'js-slang';
 import loadSourceModules from 'js-slang/dist/modules/loader';
+import { ErrorSeverity, ErrorType, Result, SourceError } from 'js-slang/dist/types';
 
+import { CseMachine } from '../../features/cseMachine/java/CseMachine';
 import Constants from './Constants';
 import DisplayBufferService from './DisplayBufferService';
 
-export async function javaRun(javaCode: string, context: Context) {
+export async function javaRun(javaCode: string, context: Context, targetStep: number, isUsingCse: boolean) {
   let compiled = {};
 
   let files = {};
@@ -86,6 +91,8 @@ export async function javaRun(javaCode: string, context: Context) {
     });
   };
 
+  if (isUsingCse) return await runJavaCseMachine(javaCode, targetStep, context);
+
   // FIXME: Remove when the compiler is working
   try {
     const json = JSON.parse(javaCode);
@@ -136,3 +143,47 @@ export async function javaRun(javaCode: string, context: Context) {
       return { status: 'error' };
     });
 }
+
+export function visualizeJavaCseMachine({ context }: { context: JavaContext }) {
+  try {
+    CseMachine.drawCse(context);
+  } catch (err) {
+    throw new Error('Java CSE machine is not enabled');
+  }
+}
+
+export async function runJavaCseMachine(
+  code: string,
+  targetStep: number,
+  context: Context
+) {
+  const convertJavaErrorToJsError = (e: JavaSourceError): SourceError => ({
+    type: ErrorType.RUNTIME,
+    severity: ErrorSeverity.ERROR,
+    location: {
+      start: {
+        line: 0,
+        column: 0,
+      },
+      end: {
+        line: 0,
+        column: 0,
+      },
+    },
+    explain: () => e.explain(),
+    elaborate: () => e.explain(),
+  });
+  context.executionMethod = 'cse-machine';
+  return runECEvaluator(code, targetStep)
+    .then(result => {
+      context.runtime.envStepsTotal = result.context.totalSteps;
+      if (result.status === 'error') {
+        context.errors = result.context.errors.map(e => convertJavaErrorToJsError(e));
+      }
+      return result;
+    })
+    .catch(e => {
+      console.error(e);
+      return { status: 'error' } as Result;
+    });
+}

From f93bf8e9b878d55d5e76de6921f98e62ac6deac2 Mon Sep 17 00:00:00 2001
From: joel chan <joelchanzhiyang@gmail.com>
Date: Fri, 12 Apr 2024 08:51:08 +0000
Subject: [PATCH 04/12] Linting errors for java-slang-csec

---
 .../controlBar/ControlBarChapterSelect.tsx    |   2 +-
 .../sagas/WorkspaceSaga/helpers/evalCode.ts   |   4 +-
 .../WorkspaceSaga/helpers/updateInspector.ts  |   2 +-
 .../content/SideContentCseMachine.tsx         | 204 ++++++++++--------
 src/commons/utils/JavaHelper.ts               |  21 +-
 src/features/cseMachine/java/CseMachine.tsx   |  21 +-
 .../cseMachine/java/components/Arrow.tsx      |  13 +-
 .../cseMachine/java/components/Binding.tsx    |  36 ++--
 .../cseMachine/java/components/Control.tsx    | 103 +++++----
 .../java/components/ControlItem.tsx           |  25 +--
 .../java/components/Environment.tsx           | 123 ++++++-----
 .../cseMachine/java/components/Frame.tsx      |  37 ++--
 .../cseMachine/java/components/Line.tsx       |  16 +-
 .../cseMachine/java/components/Method.tsx     |   6 +-
 .../cseMachine/java/components/Object.tsx     |  13 +-
 .../cseMachine/java/components/Stash.tsx      |  46 ++--
 .../cseMachine/java/components/StashItem.tsx  |  33 ++-
 .../cseMachine/java/components/Text.tsx       |  17 +-
 .../cseMachine/java/components/Variable.tsx   |  24 +--
 19 files changed, 383 insertions(+), 363 deletions(-)

diff --git a/src/commons/controlBar/ControlBarChapterSelect.tsx b/src/commons/controlBar/ControlBarChapterSelect.tsx
index 47ba376344..11759c56ca 100644
--- a/src/commons/controlBar/ControlBarChapterSelect.tsx
+++ b/src/commons/controlBar/ControlBarChapterSelect.tsx
@@ -89,7 +89,7 @@ export const ControlBarChapterSelect: React.FC<ControlBarChapterSelectProps> = (
     ...(Constants.playgroundOnly ? [fullJSLanguage, fullTSLanguage, htmlLanguage] : []),
     ...schemeLanguages,
     ...pyLanguages,
-    ...javaLanguages,
+    ...javaLanguages
   ];
 
   return (
diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts
index 3f4f6e208c..0dae58611d 100644
--- a/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts
+++ b/src/commons/sagas/WorkspaceSaga/helpers/evalCode.ts
@@ -250,9 +250,7 @@ export function* evalCode(
   let lastDebuggerResult = yield select(
     (state: OverallState) => state.workspaces[workspaceLocation].lastDebuggerResult
   );
-  const isUsingCse = yield select(
-    (state: OverallState) => state.workspaces["playground"].usingCse
-  );
+  const isUsingCse = yield select((state: OverallState) => state.workspaces['playground'].usingCse);
 
   // Handles `console.log` statements in fullJS
   const detachConsole: () => void =
diff --git a/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts b/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts
index f109aa76e9..788e813ebf 100644
--- a/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts
+++ b/src/commons/sagas/WorkspaceSaga/helpers/updateInspector.ts
@@ -12,7 +12,7 @@ export function* updateInspector(workspaceLocation: WorkspaceLocation): SagaIter
   try {
     const [lastDebuggerResult, chapter] = yield select((state: OverallState) => [
       state.workspaces[workspaceLocation].lastDebuggerResult,
-      state.workspaces[workspaceLocation].context.chapter,
+      state.workspaces[workspaceLocation].context.chapter
     ]);
     if (chapter === Chapter.FULL_JAVA) {
       const controlItem = lastDebuggerResult.context.control.peek();
diff --git a/src/commons/sideContent/content/SideContentCseMachine.tsx b/src/commons/sideContent/content/SideContentCseMachine.tsx
index e77c96a220..355f3c0cd5 100644
--- a/src/commons/sideContent/content/SideContentCseMachine.tsx
+++ b/src/commons/sideContent/content/SideContentCseMachine.tsx
@@ -90,7 +90,7 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
       height: this.calculateHeight(props.sideContentHeight),
       lastStep: false,
       stepLimitExceeded: false,
-      chapter: props.chapter,
+      chapter: props.chapter
     };
     if (this.isJava()) {
       JavaCseMachine.init(
@@ -232,45 +232,53 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
             onRelease={this.sliderRelease}
             value={this.state.value < 0 ? 0 : this.state.value}
           />
-          <div style={{ display: 'flex', justifyContent: this.isJava() ? 'center' : 'space-between', alignItems: 'center' }}>
-            {!this.isJava() && <ButtonGroup>
-              <Tooltip2 content="Control and Stash" compact>
-                <AnchorButton
-                  onMouseUp={() => {
-                    if (this.state.visualization) {
-                      CseMachine.toggleControlStash();
-                      CseMachine.redraw();
-                    }
-                  }}
-                  icon="layers"
-                  disabled={!this.state.visualization}
-                >
-                  <Checkbox
-                    checked={CseMachine.getControlStash()}
+          <div
+            style={{
+              display: 'flex',
+              justifyContent: this.isJava() ? 'center' : 'space-between',
+              alignItems: 'center'
+            }}
+          >
+            {!this.isJava() && (
+              <ButtonGroup>
+                <Tooltip2 content="Control and Stash" compact>
+                  <AnchorButton
+                    onMouseUp={() => {
+                      if (this.state.visualization) {
+                        CseMachine.toggleControlStash();
+                        CseMachine.redraw();
+                      }
+                    }}
+                    icon="layers"
                     disabled={!this.state.visualization}
-                    style={{ margin: 0 }}
-                  />
-                </AnchorButton>
-              </Tooltip2>
-              <Tooltip2 content="Truncate Control" compact>
-                <AnchorButton
-                  onMouseUp={() => {
-                    if (this.state.visualization) {
-                      CseMachine.toggleStackTruncated();
-                      CseMachine.redraw();
-                    }
-                  }}
-                  icon="minimize"
-                  disabled={!this.state.visualization}
-                >
-                  <Checkbox
-                    checked={CseMachine.getStackTruncated()}
+                  >
+                    <Checkbox
+                      checked={CseMachine.getControlStash()}
+                      disabled={!this.state.visualization}
+                      style={{ margin: 0 }}
+                    />
+                  </AnchorButton>
+                </Tooltip2>
+                <Tooltip2 content="Truncate Control" compact>
+                  <AnchorButton
+                    onMouseUp={() => {
+                      if (this.state.visualization) {
+                        CseMachine.toggleStackTruncated();
+                        CseMachine.redraw();
+                      }
+                    }}
+                    icon="minimize"
                     disabled={!this.state.visualization}
-                    style={{ margin: 0 }}
-                  />
-                </AnchorButton>
-              </Tooltip2>
-            </ButtonGroup>}
+                  >
+                    <Checkbox
+                      checked={CseMachine.getStackTruncated()}
+                      disabled={!this.state.visualization}
+                      style={{ margin: 0 }}
+                    />
+                  </AnchorButton>
+                </Tooltip2>
+              </ButtonGroup>
+            )}
             <ButtonGroup>
               <Button
                 disabled={!this.state.visualization}
@@ -281,13 +289,19 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
                 disabled={!this.state.visualization}
                 icon="chevron-left"
                 onClick={
-                  this.isJava() || CseMachine.getControlStash() ? this.stepPrevious : this.stepPrevChangepoint
+                  this.isJava() || CseMachine.getControlStash()
+                    ? this.stepPrevious
+                    : this.stepPrevChangepoint
                 }
               />
               <Button
                 disabled={!this.state.visualization}
                 icon="chevron-right"
-                onClick={this.isJava() || CseMachine.getControlStash() ? this.stepNext : this.stepNextChangepoint}
+                onClick={
+                  this.isJava() || CseMachine.getControlStash()
+                    ? this.stepNext
+                    : this.stepNextChangepoint
+                }
               />
               <Button
                 disabled={!this.state.visualization}
@@ -295,33 +309,35 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
                 onClick={this.stepNextBreakpoint}
               />
             </ButtonGroup>
-            {!this.isJava() && <ButtonGroup>
-              <Tooltip2 content="Print" compact>
-                <AnchorButton
-                  onMouseUp={() => {
-                    if (this.state.visualization) {
-                      CseMachine.togglePrintableMode();
-                      CseMachine.redraw();
-                    }
-                  }}
-                  icon="print"
-                  disabled={!this.state.visualization}
-                >
-                  <Checkbox
+            {!this.isJava() && (
+              <ButtonGroup>
+                <Tooltip2 content="Print" compact>
+                  <AnchorButton
+                    onMouseUp={() => {
+                      if (this.state.visualization) {
+                        CseMachine.togglePrintableMode();
+                        CseMachine.redraw();
+                      }
+                    }}
+                    icon="print"
+                    disabled={!this.state.visualization}
+                  >
+                    <Checkbox
+                      disabled={!this.state.visualization}
+                      checked={CseMachine.getPrintableMode()}
+                      style={{ margin: 0 }}
+                    />
+                  </AnchorButton>
+                </Tooltip2>
+                <Tooltip2 content="Save" compact>
+                  <AnchorButton
+                    icon="floppy-disk"
                     disabled={!this.state.visualization}
-                    checked={CseMachine.getPrintableMode()}
-                    style={{ margin: 0 }}
+                    onClick={Layout.exportImage}
                   />
-                </AnchorButton>
-              </Tooltip2>
-              <Tooltip2 content="Save" compact>
-                <AnchorButton
-                  icon="floppy-disk"
-                  disabled={!this.state.visualization}
-                  onClick={Layout.exportImage}
-                />
-              </Tooltip2>
-            </ButtonGroup>}
+                </Tooltip2>
+              </ButtonGroup>
+            )}
           </div>
         </div>{' '}
         {this.state.visualization &&
@@ -353,32 +369,34 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
             className={Classes.RUNNING_TEXT}
             data-testid="cse-machine-default-text"
           >
-            {this.isJava()
-            ? <span>
-              The CSEC machine generates control, stash, environment and class model diagrams adapted from the
-              notation introduced in{' '}
-              <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
-                <i>
-                  Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3,
-                  Section 2
-                </i>
-              </a>{'. '}
-
-              You have chosen the sublanguage{' '}
-              <a href={`${Links.sourceDocs}java_csec/`} rel="noopener noreferrer" target="_blank">
-                <i>Java CSEC</i>
-              </a>
-            </span>
-            : <span>
-              The CSE machine generates control, stash and environment model diagrams following a
-              notation introduced in{' '}
-              <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
-                <i>
-                  Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3,
-                  Section 2
-                </i>
-              </a>
-            </span>}
+            {this.isJava() ? (
+              <span>
+                The CSEC machine generates control, stash, environment and class model diagrams
+                adapted from the notation introduced in{' '}
+                <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
+                  <i>
+                    Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter
+                    3, Section 2
+                  </i>
+                </a>
+                {'. '}
+                You have chosen the sublanguage{' '}
+                <a href={`${Links.sourceDocs}java_csec/`} rel="noopener noreferrer" target="_blank">
+                  <i>Java CSEC</i>
+                </a>
+              </span>
+            ) : (
+              <span>
+                The CSE machine generates control, stash and environment model diagrams following a
+                notation introduced in{' '}
+                <a href={Links.textbookChapter3_2} rel="noopener noreferrer" target="_blank">
+                  <i>
+                    Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter
+                    3, Section 2
+                  </i>
+                </a>
+              </span>
+            )}
             .
             <br />
             <br /> On this tab, the REPL will be hidden from view, so do check that your code has no
@@ -431,7 +449,7 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
     } else {
       Layout.zoomStage(false, 5);
     }
-  }
+  };
 
   private sliderRelease = (newValue: number) => {
     if (newValue === this.props.stepsTotal) {
@@ -558,7 +576,7 @@ const mapStateToProps: MapStateToProps<StateProps, OwnProps, OverallState> = (
     changepointSteps: workspace.changepointSteps,
     needCseUpdate: workspace.updateCse,
     machineOutput: workspace.output,
-    chapter: workspace.context.chapter,
+    chapter: workspace.context.chapter
   };
 };
 
diff --git a/src/commons/utils/JavaHelper.ts b/src/commons/utils/JavaHelper.ts
index 0d4d0ade5f..aa90646e61 100644
--- a/src/commons/utils/JavaHelper.ts
+++ b/src/commons/utils/JavaHelper.ts
@@ -11,7 +11,12 @@ import { CseMachine } from '../../features/cseMachine/java/CseMachine';
 import Constants from './Constants';
 import DisplayBufferService from './DisplayBufferService';
 
-export async function javaRun(javaCode: string, context: Context, targetStep: number, isUsingCse: boolean) {
+export async function javaRun(
+  javaCode: string,
+  context: Context,
+  targetStep: number,
+  isUsingCse: boolean
+) {
   let compiled = {};
 
   let files = {};
@@ -152,26 +157,22 @@ export function visualizeJavaCseMachine({ context }: { context: JavaContext }) {
   }
 }
 
-export async function runJavaCseMachine(
-  code: string,
-  targetStep: number,
-  context: Context
-) {
+export async function runJavaCseMachine(code: string, targetStep: number, context: Context) {
   const convertJavaErrorToJsError = (e: JavaSourceError): SourceError => ({
     type: ErrorType.RUNTIME,
     severity: ErrorSeverity.ERROR,
     location: {
       start: {
         line: 0,
-        column: 0,
+        column: 0
       },
       end: {
         line: 0,
-        column: 0,
-      },
+        column: 0
+      }
     },
     explain: () => e.explain(),
-    elaborate: () => e.explain(),
+    elaborate: () => e.explain()
   });
   context.executionMethod = 'cse-machine';
   return runECEvaluator(code, targetStep)
diff --git a/src/features/cseMachine/java/CseMachine.tsx b/src/features/cseMachine/java/CseMachine.tsx
index 9893220420..bd2660867c 100644
--- a/src/features/cseMachine/java/CseMachine.tsx
+++ b/src/features/cseMachine/java/CseMachine.tsx
@@ -1,12 +1,12 @@
-import { Context } from "java-slang/dist/ec-evaluator/types";
-import { KonvaEventObject } from "konva/lib/Node";
-import React, { RefObject } from "react";
-import { Layer, Rect, Stage } from "react-konva";
+import { Context } from 'java-slang/dist/ec-evaluator/types';
+import { KonvaEventObject } from 'konva/lib/Node';
+import React, { RefObject } from 'react';
+import { Layer, Rect, Stage } from 'react-konva';
 
-import { Config, ShapeDefaultProps } from "./../CseMachineConfig";
-import { Control } from "./components/Control";
-import { Environment } from "./components/Environment";
-import { Stash } from "./components/Stash";
+import { Config, ShapeDefaultProps } from './../CseMachineConfig';
+import { Control } from './components/Control';
+import { Environment } from './components/Environment';
+import { Stash } from './components/Stash';
 
 type SetVis = (vis: React.ReactNode) => void;
 type SetEditorHighlightedLines = (segments: [number, number][]) => void;
@@ -28,10 +28,7 @@ export class CseMachine {
   static control: Control | undefined;
   static stash: Stash | undefined;
 
-  static init(
-    setVis: SetVis,
-    setEditorHighlightedLines: (segments: [number, number][]) => void,
-  ) {
+  static init(setVis: SetVis, setEditorHighlightedLines: (segments: [number, number][]) => void) {
     this.setVis = setVis;
     this.setEditorHighlightedLines = setEditorHighlightedLines;
   }
diff --git a/src/features/cseMachine/java/components/Arrow.tsx b/src/features/cseMachine/java/components/Arrow.tsx
index 51de860418..ddbb8dfba7 100644
--- a/src/features/cseMachine/java/components/Arrow.tsx
+++ b/src/features/cseMachine/java/components/Arrow.tsx
@@ -8,7 +8,7 @@ import {
   setHoveredCursor,
   setHoveredStyle,
   setUnhoveredCursor,
-  setUnhoveredStyle,
+  setUnhoveredStyle
 } from '../../CseMachineUtils';
 import { CseMachine } from '../CseMachine';
 
@@ -17,12 +17,7 @@ export class Arrow extends Visible implements IHoverable {
   private static readonly TO_X_INDEX = 2;
   private readonly _points: number[] = [];
 
-  constructor(
-    fromX: number,
-    fromY: number,
-    toX: number,
-    toY: number,
-  ) {
+  constructor(fromX: number, fromY: number, toX: number, toY: number) {
     super();
     this._points.push(fromX, fromY, toX, toY);
   }
@@ -30,9 +25,9 @@ export class Arrow extends Visible implements IHoverable {
   setToX(x: number) {
     this._points[Arrow.TO_X_INDEX] = x;
   }
-  
+
   onMouseEnter(e: KonvaEventObject<MouseEvent>) {
-    setHoveredStyle(e.currentTarget)
+    setHoveredStyle(e.currentTarget);
     setHoveredCursor(e.currentTarget);
   }
 
diff --git a/src/features/cseMachine/java/components/Binding.tsx b/src/features/cseMachine/java/components/Binding.tsx
index 2a20032c9e..6893d82ed2 100644
--- a/src/features/cseMachine/java/components/Binding.tsx
+++ b/src/features/cseMachine/java/components/Binding.tsx
@@ -12,17 +12,12 @@ import { Variable } from './Variable';
 /** a Binding is a key-value pair in a Frame */
 export class Binding extends Visible {
   private readonly _name: Text;
-  
+
   private readonly _value: Variable | Method | Text;
   // Only Method has arrow.
   private readonly _arrow: Arrow | undefined;
 
-  constructor(
-    name: Name, 
-    value: Value,
-    x: number,
-    y: number
-  ) {
+  constructor(name: Name, value: Value, x: number, y: number) {
     super();
 
     // Position.
@@ -34,40 +29,43 @@ export class Binding extends Visible {
       this._name = new Text(
         name + Config.VariableColon, // := is part of name
         this.x(),
-        this.y());
+        this.y()
+      );
       // Value.
       this._value = new Method(
         this._name.x() + this._name.width(),
         this.y() + this._name.height() / 2,
-        value);
+        value
+      );
       this._arrow = new Arrow(
         this._name.x() + this._name.width(),
         this._name.y() + this._name.height() / 2,
         this._value.x(),
-        this._value.y());
+        this._value.y()
+      );
     } else if (value.kind === StructType.VARIABLE) {
       // Name.
       this._name = new Text(
         name + Config.VariableColon, // := is part of name
         this.x(),
-        this.y() + Config.FontSize + Config.TextPaddingX);
+        this.y() + Config.FontSize + Config.TextPaddingX
+      );
       // Value.
-      this._value = new Variable(
-        this._name.x() + this._name.width(),
-        this.y(),
-        value);
-    } else /*if (value.kind === StructType.CLASS)*/ {
+      this._value = new Variable(this._name.x() + this._name.width(), this.y(), value);
+    } /*if (value.kind === StructType.CLASS)*/ else {
       // Dummy value as class will nvr be drawn.
       // Name.
       this._name = new Text(
         name + Config.VariableColon, // := is part of name
         this.x(),
-        this.y() + Config.FontSize + Config.TextPaddingX);
+        this.y() + Config.FontSize + Config.TextPaddingX
+      );
       // Value.
       this._value = new Text(
-        "",
+        '',
         this._name.x() + this._name.width() + Config.TextPaddingX,
-        this.y() + Config.TextPaddingX);
+        this.y() + Config.TextPaddingX
+      );
     }
 
     // Height and width.
diff --git a/src/features/cseMachine/java/components/Control.tsx b/src/features/cseMachine/java/components/Control.tsx
index 7d2a7706bc..cd6550fb2c 100644
--- a/src/features/cseMachine/java/components/Control.tsx
+++ b/src/features/cseMachine/java/components/Control.tsx
@@ -1,5 +1,5 @@
-import { astToString } from "java-slang/dist/ast/utils/astToString";
-import { Control as JavaControl } from "java-slang/dist/ec-evaluator/components"; 
+import { astToString } from 'java-slang/dist/ast/utils/astToString';
+import { Control as JavaControl } from 'java-slang/dist/ec-evaluator/components';
 import {
   BinOpInstr,
   ControlItem as JavaControlItem,
@@ -12,16 +12,16 @@ import {
   ResInstr,
   ResOverloadInstr,
   ResTypeContInstr,
-  ResTypeInstr,
-} from "java-slang/dist/ec-evaluator/types";
-import { isInstr, isNode } from "java-slang/dist/ec-evaluator/utils";
-import { Group } from "react-konva";
+  ResTypeInstr
+} from 'java-slang/dist/ec-evaluator/types';
+import { isInstr, isNode } from 'java-slang/dist/ec-evaluator/utils';
+import { Group } from 'react-konva';
 
-import { Visible } from "../../components/Visible";
-import { Config } from "../../CseMachineConfig";
-import { ControlStashConfig } from "../../CseMachineControlStashConfig";
-import { CseMachine } from "../CseMachine";
-import { ControlItem } from "./ControlItem";
+import { Visible } from '../../components/Visible';
+import { Config } from '../../CseMachineConfig';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { CseMachine } from '../CseMachine';
+import { ControlItem } from './ControlItem';
 
 export class Control extends Visible {
   private readonly _controlItems: ControlItem[] = [];
@@ -31,27 +31,30 @@ export class Control extends Visible {
 
     // Position.
     this._x = ControlStashConfig.ControlPosX;
-    this._y = ControlStashConfig.ControlPosY + ControlStashConfig.StashItemHeight + ControlStashConfig.StashItemTextPadding * 2;
+    this._y =
+      ControlStashConfig.ControlPosY +
+      ControlStashConfig.StashItemHeight +
+      ControlStashConfig.StashItemTextPadding * 2;
 
     // Create each ControlItem.
     let controlItemY: number = this._y;
     control.getStack().forEach((controlItem, index) => {
       const controlItemText = this.getControlItemString(controlItem);
-      
+
       const controlItemStroke =
-        index === control.getStack().length - 1 
-        ? Config.SA_CURRENT_ITEM
-        : ControlStashConfig.SA_WHITE;
-      
+        index === control.getStack().length - 1
+          ? Config.SA_CURRENT_ITEM
+          : ControlStashConfig.SA_WHITE;
+
       // TODO reference draw ltr?
       const controlItemReference =
         isInstr(controlItem) && controlItem.instrType === InstrType.ENV
-        ? CseMachine.environment?.frames.find(f => f.frame === (controlItem as EnvInstr).env)
-        : undefined;
+          ? CseMachine.environment?.frames.find(f => f.frame === (controlItem as EnvInstr).env)
+          : undefined;
 
       const controlItemTooltip = this.getControlItemTooltip(controlItem);
       this.getControlItemTooltip(controlItem);
-      
+
       const node = isNode(controlItem) ? controlItem : controlItem.srcNode;
       const highlightOnHover = () => {
         let start = -1;
@@ -71,7 +74,7 @@ export class Control extends Visible {
         controlItemReference,
         controlItemTooltip,
         highlightOnHover,
-        unhighlightOnHover,
+        unhighlightOnHover
       );
 
       this._controlItems.push(currControlItem);
@@ -99,21 +102,21 @@ export class Control extends Visible {
 
     switch (controlItem.instrType) {
       case InstrType.RESET:
-        return "return";
+        return 'return';
       case InstrType.ASSIGNMENT:
-        return "asgn";
+        return 'asgn';
       case InstrType.BINARY_OP:
         const binOpInstr = controlItem as BinOpInstr;
         return binOpInstr.symbol;
       case InstrType.POP:
-        return "pop";
+        return 'pop';
       case InstrType.INVOCATION:
         const appInstr = controlItem as InvInstr;
         return `invoke ${appInstr.arity}`;
       case InstrType.ENV:
-        return "env";
+        return 'env';
       case InstrType.MARKER:
-        return "mark";
+        return 'mark';
       case InstrType.EVAL_VAR:
         const evalVarInstr = controlItem as EvalVarInstr;
         return `name ${evalVarInstr.symbol}`;
@@ -122,9 +125,11 @@ export class Control extends Visible {
         return `new ${newInstr.c.frame.name}`;
       case InstrType.RES_TYPE:
         const resTypeInstr = controlItem as ResTypeInstr;
-        return `res_type ${resTypeInstr.value.kind === "Class" 
-          ? resTypeInstr.value.frame.name :
-          astToString(resTypeInstr.value)}`;
+        return `res_type ${
+          resTypeInstr.value.kind === 'Class'
+            ? resTypeInstr.value.frame.name
+            : astToString(resTypeInstr.value)
+        }`;
       case InstrType.RES_TYPE_CONT:
         const resTypeContInstr = controlItem as ResTypeContInstr;
         return `res_type_cont ${resTypeContInstr.name}`;
@@ -140,11 +145,11 @@ export class Control extends Visible {
         const resInstr = controlItem as ResInstr;
         return `res ${resInstr.name}`;
       case InstrType.DEREF:
-        return "deref";
+        return 'deref';
       default:
-        return "INSTRUCTION";
+        return 'INSTRUCTION';
     }
-  }
+  };
 
   private getControlItemTooltip = (controlItem: JavaControlItem): string => {
     if (isNode(controlItem)) {
@@ -153,21 +158,21 @@ export class Control extends Visible {
 
     switch (controlItem.instrType) {
       case InstrType.RESET:
-        return "Skip control items until marker instruction is reached";
+        return 'Skip control items until marker instruction is reached';
       case InstrType.ASSIGNMENT:
-        return "Assign value on top of stash to location on top of stash";
+        return 'Assign value on top of stash to location on top of stash';
       case InstrType.BINARY_OP:
         const binOpInstr = controlItem as BinOpInstr;
         return `Perform ${binOpInstr.symbol} on top 2 stash values`;
       case InstrType.POP:
-        return "Pop most recently pushed value from stash";
+        return 'Pop most recently pushed value from stash';
       case InstrType.INVOCATION:
         const appInstr = controlItem as InvInstr;
         return `Invoke method with ${appInstr.arity} argument${appInstr.arity === 1 ? '' : 's'}`;
       case InstrType.ENV:
-        return "Set current environment to this environment";
+        return 'Set current environment to this environment';
       case InstrType.MARKER:
-        return "Mark return address";
+        return 'Mark return address';
       case InstrType.EVAL_VAR:
         const evalVarInstr = controlItem as EvalVarInstr;
         return `name ${evalVarInstr.symbol}`;
@@ -176,27 +181,33 @@ export class Control extends Visible {
         return `Create new instance of class ${newInstr.c.frame.name}`;
       case InstrType.RES_TYPE:
         const resTypeInstr = controlItem as ResTypeInstr;
-        return `Resolve type of ${resTypeInstr.value.kind === "Class" 
-          ? resTypeInstr.value.frame.name :
-          astToString(resTypeInstr.value)}`;
+        return `Resolve type of ${
+          resTypeInstr.value.kind === 'Class'
+            ? resTypeInstr.value.frame.name
+            : astToString(resTypeInstr.value)
+        }`;
       case InstrType.RES_TYPE_CONT:
         const resTypeContInstr = controlItem as ResTypeContInstr;
         return `Resolve type of ${resTypeContInstr.name} in most recently pushed type from stash`;
       case InstrType.RES_OVERLOAD:
         const resOverloadInstr = controlItem as ResOverloadInstr;
-        return `Resolve overloading of method ${resOverloadInstr.name} with ${resOverloadInstr.arity} argument${resOverloadInstr.arity === 1 ? '' : 's'}`;
+        return `Resolve overloading of method ${resOverloadInstr.name} with ${
+          resOverloadInstr.arity
+        } argument${resOverloadInstr.arity === 1 ? '' : 's'}`;
       case InstrType.RES_OVERRIDE:
-        return "Resolve overriding of resolved method on top of stash";
+        return 'Resolve overriding of resolved method on top of stash';
       case InstrType.RES_CON_OVERLOAD:
         const resConOverloadInstr = controlItem as ResConOverloadInstr;
-        return `Resolve constructor overloading of class on stash with ${resConOverloadInstr.arity} argument${resConOverloadInstr.arity === 1 ? '' : 's'}`;
+        return `Resolve constructor overloading of class on stash with ${
+          resConOverloadInstr.arity
+        } argument${resConOverloadInstr.arity === 1 ? '' : 's'}`;
       case InstrType.RES:
         const resInstr = controlItem as ResInstr;
         return `Resolve field ${resInstr.name} of most recently pushed value from stash`;
       case InstrType.DEREF:
-        return "Dereference most recently pushed value from stash";
+        return 'Dereference most recently pushed value from stash';
       default:
-        return "INSTRUCTION";
+        return 'INSTRUCTION';
     }
-  }
+  };
 }
diff --git a/src/features/cseMachine/java/components/ControlItem.tsx b/src/features/cseMachine/java/components/ControlItem.tsx
index d50dba4d29..55fa748887 100644
--- a/src/features/cseMachine/java/components/ControlItem.tsx
+++ b/src/features/cseMachine/java/components/ControlItem.tsx
@@ -12,7 +12,7 @@ import {
   setHoveredStyle,
   setUnhoveredCursor,
   setUnhoveredStyle,
-  truncateText,
+  truncateText
 } from '../../CseMachineUtils';
 import { CseMachine } from '../CseMachine';
 import { Arrow } from './Arrow';
@@ -32,14 +32,14 @@ export class ControlItem extends Visible implements IHoverable {
 
     private readonly _tooltip: string,
     private readonly highlightOnHover: () => void,
-    private readonly unhighlightOnHover: () => void,
+    private readonly unhighlightOnHover: () => void
   ) {
     super();
 
     // Position.
     this._x = ControlStashConfig.ControlPosX;
     this._y = y;
-    
+
     // Text.
     this._text = truncateText(
       this._text,
@@ -51,8 +51,9 @@ export class ControlItem extends Visible implements IHoverable {
     this._tooltipRef = React.createRef();
 
     // Height and width.
-    this._height = getTextHeight(this._text, ControlStashConfig.ControlMaxTextWidth)
-      + ControlStashConfig.ControlItemTextPadding * 2;
+    this._height =
+      getTextHeight(this._text, ControlStashConfig.ControlMaxTextWidth) +
+      ControlStashConfig.ControlItemTextPadding * 2;
     this._width = ControlStashConfig.ControlItemWidth;
 
     // Arrow
@@ -61,14 +62,14 @@ export class ControlItem extends Visible implements IHoverable {
         this._x + this._width,
         this._y + this._height / 2,
         reference.x(),
-        reference.y() + reference.height() / 2 + reference.name.height(),
+        reference.y() + reference.height() / 2 + reference.name.height()
       );
     }
   }
 
   private isCurrentItem = (): boolean => {
     return this._stroke === Config.SA_CURRENT_ITEM;
-  }
+  };
 
   onMouseEnter = (e: KonvaEventObject<MouseEvent>): void => {
     this.highlightOnHover();
@@ -91,11 +92,11 @@ export class ControlItem extends Visible implements IHoverable {
       fontFamily: ControlStashConfig.FontFamily,
       fontSize: ControlStashConfig.FontSize,
       fontStyle: ControlStashConfig.FontStyle,
-      fontVariant: ControlStashConfig.FontVariant,
+      fontVariant: ControlStashConfig.FontVariant
     };
     const tagProps = {
       stroke: this._stroke,
-      cornerRadius: ControlStashConfig.ControlItemCornerRadius,
+      cornerRadius: ControlStashConfig.ControlItemCornerRadius
     };
     return (
       <React.Fragment key={CseMachine.key++}>
@@ -107,11 +108,7 @@ export class ControlItem extends Visible implements IHoverable {
           onMouseLeave={this.onMouseLeave}
           key={CseMachine.key++}
         >
-          <Tag
-            {...ShapeDefaultProps}
-            {...tagProps}
-            key={CseMachine.key++}
-          />
+          <Tag {...ShapeDefaultProps} {...tagProps} key={CseMachine.key++} />
           <Text
             {...ShapeDefaultProps}
             {...textProps}
diff --git a/src/features/cseMachine/java/components/Environment.tsx b/src/features/cseMachine/java/components/Environment.tsx
index 0e456c170d..f1da220ea5 100644
--- a/src/features/cseMachine/java/components/Environment.tsx
+++ b/src/features/cseMachine/java/components/Environment.tsx
@@ -1,16 +1,16 @@
-import { Environment as JavaEnvironment, EnvNode } from "java-slang/dist/ec-evaluator/components";
-import { Class as JavaClass, StructType } from "java-slang/dist/ec-evaluator/types";
-import { Group } from "react-konva";
-
-import { Visible } from "../../components/Visible";
-import { Config } from "../../CseMachineConfig";
-import { ControlStashConfig } from "../../CseMachineControlStashConfig";
-import { CseMachine } from "../CseMachine";
-import { Arrow } from "./Arrow";
-import { Frame } from "./Frame";
-import { Line } from "./Line";
-import { Object } from "./Object";
-import { Variable } from "./Variable";
+import { Environment as JavaEnvironment, EnvNode } from 'java-slang/dist/ec-evaluator/components';
+import { Class as JavaClass, StructType } from 'java-slang/dist/ec-evaluator/types';
+import { Group } from 'react-konva';
+
+import { Visible } from '../../components/Visible';
+import { Config } from '../../CseMachineConfig';
+import { ControlStashConfig } from '../../CseMachineControlStashConfig';
+import { CseMachine } from '../CseMachine';
+import { Arrow } from './Arrow';
+import { Frame } from './Frame';
+import { Line } from './Line';
+import { Object } from './Object';
+import { Variable } from './Variable';
 
 export class Environment extends Visible {
   private readonly _methodFrames: Frame[] = [];
@@ -22,25 +22,29 @@ export class Environment extends Visible {
     super();
 
     // Position.
-    this._x = ControlStashConfig.ControlPosX + ControlStashConfig.ControlItemWidth + 2 * Config.CanvasPaddingX;
-    this._y = ControlStashConfig.StashPosY + ControlStashConfig.StashItemHeight + 2 * Config.CanvasPaddingY;
+    this._x =
+      ControlStashConfig.ControlPosX +
+      ControlStashConfig.ControlItemWidth +
+      2 * Config.CanvasPaddingX;
+    this._y =
+      ControlStashConfig.StashPosY + ControlStashConfig.StashItemHeight + 2 * Config.CanvasPaddingY;
 
     // Create method frames.
     const methodFramesX = this._x;
     let methodFramesY: number = this._y;
     let methodFramesWidth = Number(Config.FrameMinWidth);
     environment.global.children.forEach(env => {
-      if (env.name.includes("(")) {
+      if (env.name.includes('(')) {
         let currEnv: EnvNode | undefined = env;
         let parentFrame;
         while (currEnv) {
           const stroke = currEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
           const frame = new Frame(currEnv, methodFramesX, methodFramesY, stroke);
           this._methodFrames.push(frame);
-          methodFramesY += (frame.height() + Config.FramePaddingY);
+          methodFramesY += frame.height() + Config.FramePaddingY;
           methodFramesWidth = Math.max(methodFramesWidth, frame.width());
           parentFrame && frame.setParent(parentFrame);
-  
+
           parentFrame = frame;
           currEnv = currEnv.children.length ? currEnv.children[0] : undefined;
         }
@@ -76,10 +80,10 @@ export class Environment extends Visible {
       }
 
       // Standardize obj frames width.
-      objectFrames.forEach(o => o.setWidth(objectFrameWidth))
+      objectFrames.forEach(o => o.setWidth(objectFrameWidth));
 
       // Only add padding btwn objects.
-      objectFramesY += Config.FramePaddingY
+      objectFramesY += Config.FramePaddingY;
 
       this._objects.push(new Object(objectFrames, obj));
     });
@@ -89,7 +93,8 @@ export class Environment extends Visible {
     let classFramesY = this._y;
     for (const [_, c] of environment.global.frame.entries()) {
       const classEnv = (c as JavaClass).frame;
-      const classFrameStroke = classEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
+      const classFrameStroke =
+        classEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
       const highlightOnHover = () => {
         const node = (c as JavaClass).classDecl;
         let start = -1;
@@ -101,14 +106,22 @@ export class Environment extends Visible {
         CseMachine.setEditorHighlightedLines([[start, end]]);
       };
       const unhighlightOnHover = () => CseMachine.setEditorHighlightedLines([]);
-      const classFrame = new Frame(classEnv, classFramesX, classFramesY, classFrameStroke, "", highlightOnHover, unhighlightOnHover);
+      const classFrame = new Frame(
+        classEnv,
+        classFramesX,
+        classFramesY,
+        classFrameStroke,
+        '',
+        highlightOnHover,
+        unhighlightOnHover
+      );
       const superClassName = (c as JavaClass).superclass?.frame.name;
       if (superClassName) {
         const parentFrame = this._classFrames.find(f => f.name.text === superClassName)!;
-        classFrame.setParent(parentFrame)
+        classFrame.setParent(parentFrame);
       }
       this._classFrames.push(classFrame);
-      classFramesY += (classFrame.height() + Config.FramePaddingY);
+      classFramesY += classFrame.height() + Config.FramePaddingY;
     }
 
     // Draw arrow for var ref in mtd frames to corresponding obj.
@@ -124,42 +137,52 @@ export class Environment extends Visible {
             matchingObj.y() + matchingObj.height() / 2
           );
         }
-      })
+      });
     });
 
     // Draw arrow for var ref in obj frames to corresponding var or obj.
-    this._objects.flatMap(obj => obj.frames).forEach(of => {
-      of.bindings.forEach(b => {
-        if (b.value instanceof Variable && b.value.variable.value.kind === StructType.VARIABLE) {
-          const variable = b.value.variable.value;
-          const matchingVariable = this._classFrames.flatMap(c => c.bindings).filter(b => b.value instanceof Variable && b.value.variable === variable)[0].value as Variable;
-          b.value.value = new Arrow(
-            b.value.x() + b.value.width() / 2,
-            b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
-            matchingVariable.x(),
-            matchingVariable.y() + matchingVariable.type.height());
-        }
-        if (b.value instanceof Variable && b.value.variable.value.kind === StructType.OBJECT) {
-          const obj = b.value.variable.value.frame;
-          const matchingObj = this._objects.find(o => o.getFrame().frame === obj)!;
-          // Variable always has a box.
-          b.value.value = new Arrow(
-            b.value.x() + b.value.width() / 2,
-            b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
-            matchingObj.x(),
-            matchingObj.y() + matchingObj.height() / 2);
-        }
-      })
-    });
+    this._objects
+      .flatMap(obj => obj.frames)
+      .forEach(of => {
+        of.bindings.forEach(b => {
+          if (b.value instanceof Variable && b.value.variable.value.kind === StructType.VARIABLE) {
+            const variable = b.value.variable.value;
+            const matchingVariable = this._classFrames
+              .flatMap(c => c.bindings)
+              .filter(b => b.value instanceof Variable && b.value.variable === variable)[0]
+              .value as Variable;
+            b.value.value = new Arrow(
+              b.value.x() + b.value.width() / 2,
+              b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
+              matchingVariable.x(),
+              matchingVariable.y() + matchingVariable.type.height()
+            );
+          }
+          if (b.value instanceof Variable && b.value.variable.value.kind === StructType.OBJECT) {
+            const obj = b.value.variable.value.frame;
+            const matchingObj = this._objects.find(o => o.getFrame().frame === obj)!;
+            // Variable always has a box.
+            b.value.value = new Arrow(
+              b.value.x() + b.value.width() / 2,
+              b.value.y() + b.value.type.height() + (b.value.height() - b.value.type.height()) / 2,
+              matchingObj.x(),
+              matchingObj.y() + matchingObj.height() / 2
+            );
+          }
+        });
+      });
 
     // Draw line for obj to class.
     this._objects.forEach(obj => {
-      const matchingClass = this._classFrames.find(c => c.name.text === obj.object.class.frame.name)!;
+      const matchingClass = this._classFrames.find(
+        c => c.name.text === obj.object.class.frame.name
+      )!;
       const line = new Line(
         obj.x() + obj.width(),
         obj.y() + obj.height() / 2,
         matchingClass.x(),
-        matchingClass.y() + matchingClass.height() / 2 + matchingClass.name.height());
+        matchingClass.y() + matchingClass.height() / 2 + matchingClass.name.height()
+      );
       this._lines.push(line);
     });
   }
diff --git a/src/features/cseMachine/java/components/Frame.tsx b/src/features/cseMachine/java/components/Frame.tsx
index 975e07e79e..8465b8b65f 100644
--- a/src/features/cseMachine/java/components/Frame.tsx
+++ b/src/features/cseMachine/java/components/Frame.tsx
@@ -29,7 +29,7 @@ export class Frame extends Visible implements IHoverable {
 
     readonly tooltip?: string,
     readonly highlightOnHover?: () => void,
-    readonly unhighlightOnHover?: () => void,
+    readonly unhighlightOnHover?: () => void
   ) {
     super();
 
@@ -46,16 +46,18 @@ export class Frame extends Visible implements IHoverable {
     for (const [key, data] of frame.frame) {
       const currBinding: Binding = new Binding(key, data, this._x + Config.FramePaddingX, bindingY);
       this.bindings.push(currBinding);
-      bindingY += (currBinding.height() + Config.FramePaddingY);
+      bindingY += currBinding.height() + Config.FramePaddingY;
       this._width = Math.max(this._width, currBinding.width() + 2 * Config.FramePaddingX);
-      this._height += (currBinding.height() + Config.FramePaddingY);
+      this._height += currBinding.height() + Config.FramePaddingY;
     }
 
     // Set x of Method aft knowing frame width.
-    this.bindings.filter(b => b.value instanceof Method).forEach(b => {
-      (b.value as Method).setX(this._x + this._width + Config.FramePaddingX);
-      b.setArrowToX(this._x + this._width + Config.FramePaddingX);
-    })
+    this.bindings
+      .filter(b => b.value instanceof Method)
+      .forEach(b => {
+        (b.value as Method).setX(this._x + this._width + Config.FramePaddingX);
+        b.setArrowToX(this._x + this._width + Config.FramePaddingX);
+      });
 
     this.tooltipRef = React.createRef();
   }
@@ -111,16 +113,17 @@ export class Frame extends Visible implements IHoverable {
         {this.bindings.map(binding => binding.draw())}
 
         {/* Frame parent */}
-        {this.parent && new Arrow(
-          this._x + Config.FramePaddingX / 2,
-          this._y + this.name.height(),
-          this.parent.x() + Config.FramePaddingX / 2,
-          // TODO WHY NEED TO ADD NAME HEIGHT?
-          this.parent.y() + this.parent.height() + this.name?.height()
-        ).draw()}
-        
+        {this.parent &&
+          new Arrow(
+            this._x + Config.FramePaddingX / 2,
+            this._y + this.name.height(),
+            this.parent.x() + Config.FramePaddingX / 2,
+            // TODO WHY NEED TO ADD NAME HEIGHT?
+            this.parent.y() + this.parent.height() + this.name?.height()
+          ).draw()}
+
         {/* Frame tooltip */}
-        {this.tooltip && 
+        {this.tooltip && (
           <Label
             x={this.x() + this.width() + ControlStashConfig.TooltipMargin}
             y={this.y() + ControlStashConfig.TooltipMargin}
@@ -143,7 +146,7 @@ export class Frame extends Visible implements IHoverable {
               key={CseMachine.key++}
             />
           </Label>
-        }
+        )}
       </Group>
     );
   }
diff --git a/src/features/cseMachine/java/components/Line.tsx b/src/features/cseMachine/java/components/Line.tsx
index ec462a6846..67323c32b5 100644
--- a/src/features/cseMachine/java/components/Line.tsx
+++ b/src/features/cseMachine/java/components/Line.tsx
@@ -8,7 +8,7 @@ import {
   setHoveredCursor,
   setHoveredStyle,
   setUnhoveredCursor,
-  setUnhoveredStyle,
+  setUnhoveredStyle
 } from '../../CseMachineUtils';
 import { CseMachine } from '../CseMachine';
 
@@ -17,12 +17,7 @@ export class Line extends Visible implements IHoverable {
   private static readonly TO_X_INDEX = 2;
   private readonly _points: number[] = [];
 
-  constructor(
-    fromX: number,
-    fromY: number,
-    toX: number,
-    toY: number,
-  ) {
+  constructor(fromX: number, fromY: number, toX: number, toY: number) {
     super();
     this._points.push(fromX, fromY, toX, toY);
   }
@@ -32,19 +27,20 @@ export class Line extends Visible implements IHoverable {
   }
 
   onMouseEnter(e: KonvaEventObject<MouseEvent>) {
-    setHoveredStyle(e.currentTarget)
+    setHoveredStyle(e.currentTarget);
     setHoveredCursor(e.currentTarget);
   }
 
   onMouseLeave(e: KonvaEventObject<MouseEvent>) {
-    setUnhoveredStyle(e.currentTarget)
+    setUnhoveredStyle(e.currentTarget);
     setUnhoveredCursor(e.currentTarget);
   }
 
   draw() {
     const path = `M ${this._points[0]} ${this._points[1]} L ${this._points[2]} ${this._points[3]}`;
     return (
-      <Group key={CseMachine.key++}
+      <Group
+        key={CseMachine.key++}
         onMouseEnter={e => this.onMouseEnter(e)}
         onMouseLeave={e => this.onMouseLeave(e)}
       >
diff --git a/src/features/cseMachine/java/components/Method.tsx b/src/features/cseMachine/java/components/Method.tsx
index f871358444..b814920666 100644
--- a/src/features/cseMachine/java/components/Method.tsx
+++ b/src/features/cseMachine/java/components/Method.tsx
@@ -12,20 +12,20 @@ import {
   getTextHeight,
   getTextWidth,
   setHoveredCursor,
-  setUnhoveredCursor,
+  setUnhoveredCursor
 } from '../../CseMachineUtils';
 import { CseMachine } from '../CseMachine';
 
 export class Method extends Visible implements IHoverable {
   private _centerX: number;
-  
+
   private readonly _tooltipRef: RefObject<any>;
   private readonly _tooltip: string;
 
   constructor(
     x: number,
     y: number,
-    private readonly _method: JavaMethod,
+    private readonly _method: JavaMethod
   ) {
     super();
 
diff --git a/src/features/cseMachine/java/components/Object.tsx b/src/features/cseMachine/java/components/Object.tsx
index 054a72944a..ed278db2df 100644
--- a/src/features/cseMachine/java/components/Object.tsx
+++ b/src/features/cseMachine/java/components/Object.tsx
@@ -9,7 +9,7 @@ import { Frame } from './Frame';
 export class Object extends Visible {
   constructor(
     private readonly _frames: Frame[],
-    private readonly _object: JavaObject,
+    private readonly _object: JavaObject
   ) {
     super();
 
@@ -19,7 +19,10 @@ export class Object extends Visible {
 
     // Height and width.
     this._height = this._frames.reduce((accHeight, currFrame) => accHeight + currFrame.height(), 0);
-    this._width = this._frames.reduce((maxWidth, currFrame) => Math.max(maxWidth, currFrame.width()), 0);
+    this._width = this._frames.reduce(
+      (maxWidth, currFrame) => Math.max(maxWidth, currFrame.width()),
+      0
+    );
   }
 
   get frames() {
@@ -35,10 +38,6 @@ export class Object extends Visible {
   }
 
   draw(): React.ReactNode {
-    return (
-      <Group key={CseMachine.key++}>
-        {this._frames.map(f => f.draw())}
-      </Group>
-    );
+    return <Group key={CseMachine.key++}>{this._frames.map(f => f.draw())}</Group>;
   }
 }
diff --git a/src/features/cseMachine/java/components/Stash.tsx b/src/features/cseMachine/java/components/Stash.tsx
index d303dc6fe0..79e3008be5 100644
--- a/src/features/cseMachine/java/components/Stash.tsx
+++ b/src/features/cseMachine/java/components/Stash.tsx
@@ -27,10 +27,11 @@ export class Stash extends Visible {
       const stashItemStroke = ControlStashConfig.SA_WHITE;
       const stashItemReference = this.getStashItemRef(stashItem);
       const currStashItem = new StashItem(
-        stashItemX, 
+        stashItemX,
         stashItemText,
         stashItemStroke,
-        stashItemReference);
+        stashItemReference
+      );
 
       this._stashItems.push(currStashItem);
       stashItemX += currStashItem.width();
@@ -42,43 +43,42 @@ export class Stash extends Visible {
   }
 
   draw(): React.ReactNode {
-    return (
-      <Group key={CseMachine.key++} >
-        {this._stashItems.map(s => s.draw())}
-      </Group>
-    );
+    return <Group key={CseMachine.key++}>{this._stashItems.map(s => s.draw())}</Group>;
   }
 
   private getStashItemString = (stashItem: JavaStashItem): string => {
     switch (stashItem.kind) {
-      case "Literal":
+      case 'Literal':
         return stashItem.literalType.value;
       case StructType.VARIABLE:
-        return "location";
+        return 'location';
       case StructType.TYPE:
         return stashItem.type;
       default:
         return stashItem.kind.toLowerCase();
     }
-  }
+  };
 
   private getStashItemRef = (stashItem: JavaStashItem) => {
     return stashItem.kind === StructType.CLOSURE
       ? CseMachine.environment &&
-        CseMachine.environment.classes
-          .flatMap(c => c.bindings)
-          .find(b => b.value instanceof Method && b.value.method === stashItem)?.value as Method
+          (CseMachine.environment.classes
+            .flatMap(c => c.bindings)
+            .find(b => b.value instanceof Method && b.value.method === stashItem)?.value as Method)
       : stashItem.kind === StructType.VARIABLE
-      ? CseMachine.environment && 
-        (CseMachine.environment.frames
-          .flatMap(c => c.bindings)
-          .find(b => b.value instanceof Variable && b.value.variable === stashItem)?.value as Variable ||
-        CseMachine.environment.classes
+      ? CseMachine.environment &&
+        ((CseMachine.environment.frames
           .flatMap(c => c.bindings)
-          .find(b => b.value instanceof Variable && b.value.variable === stashItem)?.value as Variable ||
-        CseMachine.environment.objects
-          .flatMap(o => o.bindings)
-          .find(b => b.value instanceof Variable && b.value.variable === stashItem)?.value as Variable)
+          .find(b => b.value instanceof Variable && b.value.variable === stashItem)
+          ?.value as Variable) ||
+          (CseMachine.environment.classes
+            .flatMap(c => c.bindings)
+            .find(b => b.value instanceof Variable && b.value.variable === stashItem)
+            ?.value as Variable) ||
+          (CseMachine.environment.objects
+            .flatMap(o => o.bindings)
+            .find(b => b.value instanceof Variable && b.value.variable === stashItem)
+            ?.value as Variable))
       : stashItem.kind === StructType.CLASS
       ? CseMachine.environment &&
         CseMachine.environment.classes.find(c => c.frame === stashItem.frame)
@@ -86,5 +86,5 @@ export class Stash extends Visible {
       ? CseMachine.environment &&
         CseMachine.environment.objects.find(o => o.frame === stashItem.frame)
       : undefined;
-  }
+  };
 }
diff --git a/src/features/cseMachine/java/components/StashItem.tsx b/src/features/cseMachine/java/components/StashItem.tsx
index 2cdb91d8f1..27646641e6 100644
--- a/src/features/cseMachine/java/components/StashItem.tsx
+++ b/src/features/cseMachine/java/components/StashItem.tsx
@@ -3,7 +3,7 @@ import {
   Group as KonvaGroup,
   Label as KonvaLabel,
   Tag as KonvaTag,
-  Text as KonvaText,
+  Text as KonvaText
 } from 'react-konva';
 
 import { Visible } from '../../components/Visible';
@@ -23,14 +23,14 @@ export class StashItem extends Visible {
     x: number,
     private readonly _text: string,
     private readonly _stroke: string,
-    reference?: Method | Frame | Variable,
+    reference?: Method | Frame | Variable
   ) {
     super();
 
     // Position.
     this._x = x;
     this._y = ControlStashConfig.StashPosY;
-    
+
     // Height and width.
     this._height = ControlStashConfig.StashItemHeight + ControlStashConfig.StashItemTextPadding * 2;
     this._width = ControlStashConfig.StashItemTextPadding * 2 + getTextWidth(this._text);
@@ -39,15 +39,16 @@ export class StashItem extends Visible {
     if (reference) {
       const toY =
         reference instanceof Frame
-        ? reference.y() + reference.name.height()
-        : reference instanceof Method
-        ? reference.y()
-        : reference.y() + reference.type.height();
+          ? reference.y() + reference.name.height()
+          : reference instanceof Method
+          ? reference.y()
+          : reference.y() + reference.type.height();
       this._arrow = new Arrow(
         this._x + this._width / 2,
         this._y + this._height,
         reference.x(),
-        toY);
+        toY
+      );
     }
   }
 
@@ -58,27 +59,19 @@ export class StashItem extends Visible {
       fontFamily: ControlStashConfig.FontFamily,
       fontSize: ControlStashConfig.FontSize,
       fontStyle: ControlStashConfig.FontStyle,
-      fontVariant: ControlStashConfig.FontVariant,
+      fontVariant: ControlStashConfig.FontVariant
     };
 
     const tagProps = {
       stroke: this._stroke,
-      cornerRadius: ControlStashConfig.StashItemCornerRadius,
+      cornerRadius: ControlStashConfig.StashItemCornerRadius
     };
 
     return (
       <KonvaGroup key={CseMachine.key++}>
         {/* Text */}
-        <KonvaLabel
-          x={this.x()}
-          y={this.y()}
-          key={CseMachine.key++}
-        >
-          <KonvaTag
-            {...ShapeDefaultProps}
-            {...tagProps}
-            key={CseMachine.key++}
-          />
+        <KonvaLabel x={this.x()} y={this.y()} key={CseMachine.key++}>
+          <KonvaTag {...ShapeDefaultProps} {...tagProps} key={CseMachine.key++} />
           <KonvaText
             {...ShapeDefaultProps}
             {...textProps}
diff --git a/src/features/cseMachine/java/components/Text.tsx b/src/features/cseMachine/java/components/Text.tsx
index 89d10c3366..db11a00abe 100644
--- a/src/features/cseMachine/java/components/Text.tsx
+++ b/src/features/cseMachine/java/components/Text.tsx
@@ -11,7 +11,7 @@ export class Text extends Visible {
   constructor(
     private readonly _text: string,
     x: number,
-    y: number,
+    y: number
   ) {
     super();
 
@@ -37,22 +37,13 @@ export class Text extends Visible {
       fontFamily: Config.FontFamily,
       fontSize: Config.FontSize,
       fontStyle: Config.FontStyle,
-      fill: Config.SA_WHITE,
+      fill: Config.SA_WHITE
     };
 
     return (
       <KonvaGroup key={CseMachine.key++}>
-        <KonvaLabel
-          x={this.x()}
-          y={this.y()}
-          key={CseMachine.key++}
-        >
-          <KonvaText
-            {...ShapeDefaultProps} 
-            key={CseMachine.key++} 
-            text={this._text}
-            {...props}
-          />
+        <KonvaLabel x={this.x()} y={this.y()} key={CseMachine.key++}>
+          <KonvaText {...ShapeDefaultProps} key={CseMachine.key++} text={this._text} {...props} />
         </KonvaLabel>
       </KonvaGroup>
     );
diff --git a/src/features/cseMachine/java/components/Variable.tsx b/src/features/cseMachine/java/components/Variable.tsx
index a784db2f7e..798cf0965f 100644
--- a/src/features/cseMachine/java/components/Variable.tsx
+++ b/src/features/cseMachine/java/components/Variable.tsx
@@ -33,7 +33,7 @@ export class Variable extends Visible {
   constructor(
     x: number,
     y: number,
-    private readonly _variable: JavaVariable,
+    private readonly _variable: JavaVariable
   ) {
     super();
 
@@ -42,29 +42,29 @@ export class Variable extends Visible {
     this._y = y;
 
     // Type.
-    this._type = new Text(
-      this._variable.type,
-      this._x,
-      this._y);
+    this._type = new Text(this._variable.type, this._x, this._y);
 
     // Value.
-    if (this.variable.value.kind === "Literal") {
+    if (this.variable.value.kind === 'Literal') {
       this._value = new Text(
         this.variable.value.literalType.value,
         this._x + Config.TextPaddingX,
-        this._y + this._type.height() + Config.TextPaddingX);
+        this._y + this._type.height() + Config.TextPaddingX
+      );
     } else if (this.variable.value.kind === StructType.SYMBOL) {
       this._value = new Text(
-        "",
+        '',
         this._x + Config.TextPaddingX,
-        this._y + this._type.height()  + Config.TextPaddingX);
+        this._y + this._type.height() + Config.TextPaddingX
+      );
     } else {
       this._value = new Text(
-        "",
+        '',
         this._x + Config.TextPaddingX,
-        this._y + this._type.height()  + Config.TextPaddingX);
+        this._y + this._type.height() + Config.TextPaddingX
+      );
     }
-    
+
     // Height and width.
     this._height = this._type.height() + this._value.height() + 2 * Config.TextPaddingX;
     this._width = Math.max(this._type.width(), this._value.width() + 2 * Config.TextPaddingX);

From 4c9b21008c0bfdafa43c01b8d6ab08219d2845e3 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 08:55:38 +0800
Subject: [PATCH 05/12] Fix frontend test: rename Object to Obj to avoid
 potential name clash?

---
 src/features/cseMachine/java/components/Environment.tsx | 6 +++---
 src/features/cseMachine/java/components/Object.tsx      | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/features/cseMachine/java/components/Environment.tsx b/src/features/cseMachine/java/components/Environment.tsx
index f1da220ea5..817a6ee234 100644
--- a/src/features/cseMachine/java/components/Environment.tsx
+++ b/src/features/cseMachine/java/components/Environment.tsx
@@ -9,12 +9,12 @@ import { CseMachine } from '../CseMachine';
 import { Arrow } from './Arrow';
 import { Frame } from './Frame';
 import { Line } from './Line';
-import { Object } from './Object';
+import { Obj } from './Object';
 import { Variable } from './Variable';
 
 export class Environment extends Visible {
   private readonly _methodFrames: Frame[] = [];
-  private readonly _objects: Object[] = [];
+  private readonly _objects: Obj[] = [];
   private readonly _classFrames: Frame[] = [];
   private readonly _lines: Line[] = [];
 
@@ -85,7 +85,7 @@ export class Environment extends Visible {
       // Only add padding btwn objects.
       objectFramesY += Config.FramePaddingY;
 
-      this._objects.push(new Object(objectFrames, obj));
+      this._objects.push(new Obj(objectFrames, obj));
     });
 
     // Create class frames.
diff --git a/src/features/cseMachine/java/components/Object.tsx b/src/features/cseMachine/java/components/Object.tsx
index ed278db2df..25c06d19a4 100644
--- a/src/features/cseMachine/java/components/Object.tsx
+++ b/src/features/cseMachine/java/components/Object.tsx
@@ -6,7 +6,7 @@ import { Visible } from '../../components/Visible';
 import { CseMachine } from '../CseMachine';
 import { Frame } from './Frame';
 
-export class Object extends Visible {
+export class Obj extends Visible {
   constructor(
     private readonly _frames: Frame[],
     private readonly _object: JavaObject

From 0693cb768130ee97bb145aebc32b3fbbc31eb039 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 09:54:02 +0800
Subject: [PATCH 06/12] Update snapshot

---
 .../SideContentCseMachine.tsx.snap            | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/commons/sideContent/__tests__/__snapshots__/SideContentCseMachine.tsx.snap b/src/commons/sideContent/__tests__/__snapshots__/SideContentCseMachine.tsx.snap
index 3ded2a0fb5..f33e89daec 100644
--- a/src/commons/sideContent/__tests__/__snapshots__/SideContentCseMachine.tsx.snap
+++ b/src/commons/sideContent/__tests__/__snapshots__/SideContentCseMachine.tsx.snap
@@ -352,17 +352,19 @@ exports[`CSE Machine component renders correctly 1`] = `
     data-testid="cse-machine-default-text"
     id="cse-machine-default-text"
   >
-    The CSE machine generates control, stash and environment model diagrams following a notation introduced in
-     
-    <a
-      href="https://sourceacademy.org/sicpjs/3.2"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      <i>
-        Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3, Section 2
-      </i>
-    </a>
+    <span>
+      The CSE machine generates control, stash and environment model diagrams following a notation introduced in
+       
+      <a
+        href="https://sourceacademy.org/sicpjs/3.2"
+        rel="noopener noreferrer"
+        target="_blank"
+      >
+        <i>
+          Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3, Section 2
+        </i>
+      </a>
+    </span>
     .
     <br />
     <br />

From af547e97c979d3cbd201b1553c9b8bfe5600b383 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 18:19:27 +0800
Subject: [PATCH 07/12] Update imports from java-slang

---
 src/commons/utils/JavaHelper.ts               |  11 +-
 src/features/cseMachine/java/CseMachine.tsx   |   4 +-
 .../cseMachine/java/components/Binding.tsx    |   8 +-
 .../cseMachine/java/components/Control.tsx    | 134 ++++++++----------
 .../java/components/Environment.tsx           |  21 ++-
 .../cseMachine/java/components/Frame.tsx      |   4 +-
 .../cseMachine/java/components/Method.tsx     |   5 +-
 .../cseMachine/java/components/Object.tsx     |   4 +-
 .../cseMachine/java/components/Stash.tsx      |  21 ++-
 .../cseMachine/java/components/Variable.tsx   |   6 +-
 10 files changed, 98 insertions(+), 120 deletions(-)

diff --git a/src/commons/utils/JavaHelper.ts b/src/commons/utils/JavaHelper.ts
index 7ea2344e5b..493fcbfd81 100644
--- a/src/commons/utils/JavaHelper.ts
+++ b/src/commons/utils/JavaHelper.ts
@@ -1,7 +1,4 @@
-import { SourceError as JavaSourceError } from 'java-slang/dist/ec-evaluator/errors';
-import { runECEvaluator } from 'java-slang/dist/ec-evaluator/index';
-import { Context as JavaContext } from 'java-slang/dist/ec-evaluator/types';
-import { compileFromSource, typeCheck } from 'java-slang';
+import { compileFromSource, ECE, typeCheck } from 'java-slang';
 import { BinaryWriter } from 'java-slang/dist/compiler/binary-writer';
 import setupJVM, { parseBin } from 'java-slang/dist/jvm';
 import { createModuleProxy, loadCachedFiles } from 'java-slang/dist/jvm/utils/integration';
@@ -161,7 +158,7 @@ export async function javaRun(
     });
 }
 
-export function visualizeJavaCseMachine({ context }: { context: JavaContext }) {
+export function visualizeJavaCseMachine({ context }: { context: ECE.Context }) {
   try {
     CseMachine.drawCse(context);
   } catch (err) {
@@ -170,7 +167,7 @@ export function visualizeJavaCseMachine({ context }: { context: JavaContext }) {
 }
 
 export async function runJavaCseMachine(code: string, targetStep: number, context: Context) {
-  const convertJavaErrorToJsError = (e: JavaSourceError): SourceError => ({
+  const convertJavaErrorToJsError = (e: ECE.SourceError): SourceError => ({
     type: ErrorType.RUNTIME,
     severity: ErrorSeverity.ERROR,
     location: {
@@ -187,7 +184,7 @@ export async function runJavaCseMachine(code: string, targetStep: number, contex
     elaborate: () => e.explain()
   });
   context.executionMethod = 'cse-machine';
-  return runECEvaluator(code, targetStep)
+  return ECE.runECEvaluator(code, targetStep)
     .then(result => {
       context.runtime.envStepsTotal = result.context.totalSteps;
       if (result.status === 'error') {
diff --git a/src/features/cseMachine/java/CseMachine.tsx b/src/features/cseMachine/java/CseMachine.tsx
index bd2660867c..d259b4e021 100644
--- a/src/features/cseMachine/java/CseMachine.tsx
+++ b/src/features/cseMachine/java/CseMachine.tsx
@@ -1,4 +1,4 @@
-import { Context } from 'java-slang/dist/ec-evaluator/types';
+import { ECE } from 'java-slang';
 import { KonvaEventObject } from 'konva/lib/Node';
 import React, { RefObject } from 'react';
 import { Layer, Rect, Stage } from 'react-konva';
@@ -35,7 +35,7 @@ export class CseMachine {
 
   /** updates the visualization state in the SideContentCseMachine component based on
    * the Java Slang context passed in */
-  static drawCse(context: Context) {
+  static drawCse(context: ECE.Context) {
     if (!this.setVis || !context.environment || !context.control || !context.stash) {
       throw new Error('Java CSE Machine not initialized');
     }
diff --git a/src/features/cseMachine/java/components/Binding.tsx b/src/features/cseMachine/java/components/Binding.tsx
index 6893d82ed2..6ecdb6cb47 100644
--- a/src/features/cseMachine/java/components/Binding.tsx
+++ b/src/features/cseMachine/java/components/Binding.tsx
@@ -1,4 +1,4 @@
-import { Name, StructType, Value } from 'java-slang/dist/ec-evaluator/types';
+import { ECE } from 'java-slang';
 import React from 'react';
 
 import { Visible } from '../../components/Visible';
@@ -17,14 +17,14 @@ export class Binding extends Visible {
   // Only Method has arrow.
   private readonly _arrow: Arrow | undefined;
 
-  constructor(name: Name, value: Value, x: number, y: number) {
+  constructor(name: ECE.Name, value: ECE.Value, x: number, y: number) {
     super();
 
     // Position.
     this._x = x;
     this._y = y;
 
-    if (value.kind === StructType.CLOSURE) {
+    if (value.kind === ECE.StructType.CLOSURE) {
       // Name.
       this._name = new Text(
         name + Config.VariableColon, // := is part of name
@@ -43,7 +43,7 @@ export class Binding extends Visible {
         this._value.x(),
         this._value.y()
       );
-    } else if (value.kind === StructType.VARIABLE) {
+    } else if (value.kind === ECE.StructType.VARIABLE) {
       // Name.
       this._name = new Text(
         name + Config.VariableColon, // := is part of name
diff --git a/src/features/cseMachine/java/components/Control.tsx b/src/features/cseMachine/java/components/Control.tsx
index cd6550fb2c..8305ec1551 100644
--- a/src/features/cseMachine/java/components/Control.tsx
+++ b/src/features/cseMachine/java/components/Control.tsx
@@ -1,20 +1,4 @@
-import { astToString } from 'java-slang/dist/ast/utils/astToString';
-import { Control as JavaControl } from 'java-slang/dist/ec-evaluator/components';
-import {
-  BinOpInstr,
-  ControlItem as JavaControlItem,
-  EnvInstr,
-  EvalVarInstr,
-  InstrType,
-  InvInstr,
-  NewInstr,
-  ResConOverloadInstr,
-  ResInstr,
-  ResOverloadInstr,
-  ResTypeContInstr,
-  ResTypeInstr
-} from 'java-slang/dist/ec-evaluator/types';
-import { isInstr, isNode } from 'java-slang/dist/ec-evaluator/utils';
+import { astToString, ECE } from 'java-slang';
 import { Group } from 'react-konva';
 
 import { Visible } from '../../components/Visible';
@@ -26,7 +10,7 @@ import { ControlItem } from './ControlItem';
 export class Control extends Visible {
   private readonly _controlItems: ControlItem[] = [];
 
-  constructor(control: JavaControl) {
+  constructor(control: ECE.Control) {
     super();
 
     // Position.
@@ -48,14 +32,14 @@ export class Control extends Visible {
 
       // TODO reference draw ltr?
       const controlItemReference =
-        isInstr(controlItem) && controlItem.instrType === InstrType.ENV
-          ? CseMachine.environment?.frames.find(f => f.frame === (controlItem as EnvInstr).env)
+        ECE.isInstr(controlItem) && controlItem.instrType === ECE.InstrType.ENV
+          ? CseMachine.environment?.frames.find(f => f.frame === (controlItem as ECE.EnvInstr).env)
           : undefined;
 
       const controlItemTooltip = this.getControlItemTooltip(controlItem);
       this.getControlItemTooltip(controlItem);
 
-      const node = isNode(controlItem) ? controlItem : controlItem.srcNode;
+      const node = ECE.isNode(controlItem) ? controlItem : controlItem.srcNode;
       const highlightOnHover = () => {
         let start = -1;
         let end = -1;
@@ -95,116 +79,116 @@ export class Control extends Visible {
     );
   }
 
-  private getControlItemString = (controlItem: JavaControlItem): string => {
-    if (isNode(controlItem)) {
+  private getControlItemString = (controlItem: ECE.ControlItem): string => {
+    if (ECE.isNode(controlItem)) {
       return astToString(controlItem);
     }
 
     switch (controlItem.instrType) {
-      case InstrType.RESET:
+      case ECE.InstrType.RESET:
         return 'return';
-      case InstrType.ASSIGNMENT:
+      case ECE.InstrType.ASSIGNMENT:
         return 'asgn';
-      case InstrType.BINARY_OP:
-        const binOpInstr = controlItem as BinOpInstr;
+      case ECE.InstrType.BINARY_OP:
+        const binOpInstr = controlItem as ECE.BinOpInstr;
         return binOpInstr.symbol;
-      case InstrType.POP:
+      case ECE.InstrType.POP:
         return 'pop';
-      case InstrType.INVOCATION:
-        const appInstr = controlItem as InvInstr;
+      case ECE.InstrType.INVOCATION:
+        const appInstr = controlItem as ECE.InvInstr;
         return `invoke ${appInstr.arity}`;
-      case InstrType.ENV:
+      case ECE.InstrType.ENV:
         return 'env';
-      case InstrType.MARKER:
+      case ECE.InstrType.MARKER:
         return 'mark';
-      case InstrType.EVAL_VAR:
-        const evalVarInstr = controlItem as EvalVarInstr;
+      case ECE.InstrType.EVAL_VAR:
+        const evalVarInstr = controlItem as ECE.EvalVarInstr;
         return `name ${evalVarInstr.symbol}`;
-      case InstrType.NEW:
-        const newInstr = controlItem as NewInstr;
+      case ECE.InstrType.NEW:
+        const newInstr = controlItem as ECE.NewInstr;
         return `new ${newInstr.c.frame.name}`;
-      case InstrType.RES_TYPE:
-        const resTypeInstr = controlItem as ResTypeInstr;
+      case ECE.InstrType.RES_TYPE:
+        const resTypeInstr = controlItem as ECE.ResTypeInstr;
         return `res_type ${
           resTypeInstr.value.kind === 'Class'
             ? resTypeInstr.value.frame.name
             : astToString(resTypeInstr.value)
         }`;
-      case InstrType.RES_TYPE_CONT:
-        const resTypeContInstr = controlItem as ResTypeContInstr;
+      case ECE.InstrType.RES_TYPE_CONT:
+        const resTypeContInstr = controlItem as ECE.ResTypeContInstr;
         return `res_type_cont ${resTypeContInstr.name}`;
-      case InstrType.RES_OVERLOAD:
-        const resOverloadInstr = controlItem as ResOverloadInstr;
+      case ECE.InstrType.RES_OVERLOAD:
+        const resOverloadInstr = controlItem as ECE.ResOverloadInstr;
         return `res_overload ${resOverloadInstr.name} ${resOverloadInstr.arity}`;
-      case InstrType.RES_OVERRIDE:
+      case ECE.InstrType.RES_OVERRIDE:
         return `res_override`;
-      case InstrType.RES_CON_OVERLOAD:
-        const resConOverloadInstr = controlItem as ResConOverloadInstr;
+      case ECE.InstrType.RES_CON_OVERLOAD:
+        const resConOverloadInstr = controlItem as ECE.ResConOverloadInstr;
         return `res_con_overload ${resConOverloadInstr.arity}`;
-      case InstrType.RES:
-        const resInstr = controlItem as ResInstr;
+      case ECE.InstrType.RES:
+        const resInstr = controlItem as ECE.ResInstr;
         return `res ${resInstr.name}`;
-      case InstrType.DEREF:
+      case ECE.InstrType.DEREF:
         return 'deref';
       default:
         return 'INSTRUCTION';
     }
   };
 
-  private getControlItemTooltip = (controlItem: JavaControlItem): string => {
-    if (isNode(controlItem)) {
+  private getControlItemTooltip = (controlItem: ECE.ControlItem): string => {
+    if (ECE.isNode(controlItem)) {
       return astToString(controlItem);
     }
 
     switch (controlItem.instrType) {
-      case InstrType.RESET:
+      case ECE.InstrType.RESET:
         return 'Skip control items until marker instruction is reached';
-      case InstrType.ASSIGNMENT:
+      case ECE.InstrType.ASSIGNMENT:
         return 'Assign value on top of stash to location on top of stash';
-      case InstrType.BINARY_OP:
-        const binOpInstr = controlItem as BinOpInstr;
+      case ECE.InstrType.BINARY_OP:
+        const binOpInstr = controlItem as ECE.BinOpInstr;
         return `Perform ${binOpInstr.symbol} on top 2 stash values`;
-      case InstrType.POP:
+      case ECE.InstrType.POP:
         return 'Pop most recently pushed value from stash';
-      case InstrType.INVOCATION:
-        const appInstr = controlItem as InvInstr;
+      case ECE.InstrType.INVOCATION:
+        const appInstr = controlItem as ECE.InvInstr;
         return `Invoke method with ${appInstr.arity} argument${appInstr.arity === 1 ? '' : 's'}`;
-      case InstrType.ENV:
+      case ECE.InstrType.ENV:
         return 'Set current environment to this environment';
-      case InstrType.MARKER:
+      case ECE.InstrType.MARKER:
         return 'Mark return address';
-      case InstrType.EVAL_VAR:
-        const evalVarInstr = controlItem as EvalVarInstr;
+      case ECE.InstrType.EVAL_VAR:
+        const evalVarInstr = controlItem as ECE.EvalVarInstr;
         return `name ${evalVarInstr.symbol}`;
-      case InstrType.NEW:
-        const newInstr = controlItem as NewInstr;
+      case ECE.InstrType.NEW:
+        const newInstr = controlItem as ECE.NewInstr;
         return `Create new instance of class ${newInstr.c.frame.name}`;
-      case InstrType.RES_TYPE:
-        const resTypeInstr = controlItem as ResTypeInstr;
+      case ECE.InstrType.RES_TYPE:
+        const resTypeInstr = controlItem as ECE.ResTypeInstr;
         return `Resolve type of ${
           resTypeInstr.value.kind === 'Class'
             ? resTypeInstr.value.frame.name
             : astToString(resTypeInstr.value)
         }`;
-      case InstrType.RES_TYPE_CONT:
-        const resTypeContInstr = controlItem as ResTypeContInstr;
+      case ECE.InstrType.RES_TYPE_CONT:
+        const resTypeContInstr = controlItem as ECE.ResTypeContInstr;
         return `Resolve type of ${resTypeContInstr.name} in most recently pushed type from stash`;
-      case InstrType.RES_OVERLOAD:
-        const resOverloadInstr = controlItem as ResOverloadInstr;
+      case ECE.InstrType.RES_OVERLOAD:
+        const resOverloadInstr = controlItem as ECE.ResOverloadInstr;
         return `Resolve overloading of method ${resOverloadInstr.name} with ${
           resOverloadInstr.arity
         } argument${resOverloadInstr.arity === 1 ? '' : 's'}`;
-      case InstrType.RES_OVERRIDE:
+      case ECE.InstrType.RES_OVERRIDE:
         return 'Resolve overriding of resolved method on top of stash';
-      case InstrType.RES_CON_OVERLOAD:
-        const resConOverloadInstr = controlItem as ResConOverloadInstr;
+      case ECE.InstrType.RES_CON_OVERLOAD:
+        const resConOverloadInstr = controlItem as ECE.ResConOverloadInstr;
         return `Resolve constructor overloading of class on stash with ${
           resConOverloadInstr.arity
         } argument${resConOverloadInstr.arity === 1 ? '' : 's'}`;
-      case InstrType.RES:
-        const resInstr = controlItem as ResInstr;
+      case ECE.InstrType.RES:
+        const resInstr = controlItem as ECE.ResInstr;
         return `Resolve field ${resInstr.name} of most recently pushed value from stash`;
-      case InstrType.DEREF:
+      case ECE.InstrType.DEREF:
         return 'Dereference most recently pushed value from stash';
       default:
         return 'INSTRUCTION';
diff --git a/src/features/cseMachine/java/components/Environment.tsx b/src/features/cseMachine/java/components/Environment.tsx
index 817a6ee234..a66f963eb6 100644
--- a/src/features/cseMachine/java/components/Environment.tsx
+++ b/src/features/cseMachine/java/components/Environment.tsx
@@ -1,5 +1,4 @@
-import { Environment as JavaEnvironment, EnvNode } from 'java-slang/dist/ec-evaluator/components';
-import { Class as JavaClass, StructType } from 'java-slang/dist/ec-evaluator/types';
+import { ECE } from 'java-slang';
 import { Group } from 'react-konva';
 
 import { Visible } from '../../components/Visible';
@@ -18,7 +17,7 @@ export class Environment extends Visible {
   private readonly _classFrames: Frame[] = [];
   private readonly _lines: Line[] = [];
 
-  constructor(environment: JavaEnvironment) {
+  constructor(environment: ECE.Environment) {
     super();
 
     // Position.
@@ -35,7 +34,7 @@ export class Environment extends Visible {
     let methodFramesWidth = Number(Config.FrameMinWidth);
     environment.global.children.forEach(env => {
       if (env.name.includes('(')) {
-        let currEnv: EnvNode | undefined = env;
+        let currEnv: ECE.EnvNode | undefined = env;
         let parentFrame;
         while (currEnv) {
           const stroke = currEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
@@ -60,7 +59,7 @@ export class Environment extends Visible {
       let objectFrameWidth = Number(Config.FrameMinWidth);
 
       // Get top env.
-      let env: EnvNode | undefined = obj.frame;
+      let env: ECE.EnvNode | undefined = obj.frame;
       while (env.parent) {
         env = env.parent;
       }
@@ -92,11 +91,11 @@ export class Environment extends Visible {
     const classFramesX = objectFramesX + objectFramesWidth + Config.FrameMinWidth;
     let classFramesY = this._y;
     for (const [_, c] of environment.global.frame.entries()) {
-      const classEnv = (c as JavaClass).frame;
+      const classEnv = (c as ECE.Class).frame;
       const classFrameStroke =
         classEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;
       const highlightOnHover = () => {
-        const node = (c as JavaClass).classDecl;
+        const node = (c as ECE.Class).classDecl;
         let start = -1;
         let end = -1;
         if (node.location) {
@@ -115,7 +114,7 @@ export class Environment extends Visible {
         highlightOnHover,
         unhighlightOnHover
       );
-      const superClassName = (c as JavaClass).superclass?.frame.name;
+      const superClassName = (c as ECE.Class).superclass?.frame.name;
       if (superClassName) {
         const parentFrame = this._classFrames.find(f => f.name.text === superClassName)!;
         classFrame.setParent(parentFrame);
@@ -127,7 +126,7 @@ export class Environment extends Visible {
     // Draw arrow for var ref in mtd frames to corresponding obj.
     this._methodFrames.forEach(mf => {
       mf.bindings.forEach(b => {
-        if (b.value instanceof Variable && b.value.variable.value.kind === StructType.OBJECT) {
+        if (b.value instanceof Variable && b.value.variable.value.kind === ECE.StructType.OBJECT) {
           const objFrame = b.value.variable.value.frame;
           const matchingObj = this._objects.filter(o => o.getFrame().frame === objFrame)[0];
           b.value.value = new Arrow(
@@ -145,7 +144,7 @@ export class Environment extends Visible {
       .flatMap(obj => obj.frames)
       .forEach(of => {
         of.bindings.forEach(b => {
-          if (b.value instanceof Variable && b.value.variable.value.kind === StructType.VARIABLE) {
+          if (b.value instanceof Variable && b.value.variable.value.kind === ECE.StructType.VARIABLE) {
             const variable = b.value.variable.value;
             const matchingVariable = this._classFrames
               .flatMap(c => c.bindings)
@@ -158,7 +157,7 @@ export class Environment extends Visible {
               matchingVariable.y() + matchingVariable.type.height()
             );
           }
-          if (b.value instanceof Variable && b.value.variable.value.kind === StructType.OBJECT) {
+          if (b.value instanceof Variable && b.value.variable.value.kind === ECE.StructType.OBJECT) {
             const obj = b.value.variable.value.frame;
             const matchingObj = this._objects.find(o => o.getFrame().frame === obj)!;
             // Variable always has a box.
diff --git a/src/features/cseMachine/java/components/Frame.tsx b/src/features/cseMachine/java/components/Frame.tsx
index 8465b8b65f..2c7ec35570 100644
--- a/src/features/cseMachine/java/components/Frame.tsx
+++ b/src/features/cseMachine/java/components/Frame.tsx
@@ -1,4 +1,4 @@
-import { EnvNode } from 'java-slang/dist/ec-evaluator/components';
+import { ECE } from 'java-slang';
 import { KonvaEventObject } from 'konva/lib/Node';
 import React, { RefObject } from 'react';
 import { Group, Label, Rect, Tag, Text as KonvaText } from 'react-konva';
@@ -22,7 +22,7 @@ export class Frame extends Visible implements IHoverable {
   private parent: Frame | undefined;
 
   constructor(
-    readonly frame: EnvNode,
+    readonly frame: ECE.EnvNode,
     x: number,
     y: number,
     readonly stroke: string,
diff --git a/src/features/cseMachine/java/components/Method.tsx b/src/features/cseMachine/java/components/Method.tsx
index b814920666..45846e10e6 100644
--- a/src/features/cseMachine/java/components/Method.tsx
+++ b/src/features/cseMachine/java/components/Method.tsx
@@ -1,5 +1,4 @@
-import { astToString } from 'java-slang/dist/ast/utils/astToString';
-import { Closure as JavaMethod } from 'java-slang/dist/ec-evaluator/types';
+import { astToString, ECE } from 'java-slang';
 import { KonvaEventObject } from 'konva/lib/Node';
 import React, { RefObject } from 'react';
 import { Circle, Group, Label, Tag, Text } from 'react-konva';
@@ -25,7 +24,7 @@ export class Method extends Visible implements IHoverable {
   constructor(
     x: number,
     y: number,
-    private readonly _method: JavaMethod
+    private readonly _method: ECE.Closure
   ) {
     super();
 
diff --git a/src/features/cseMachine/java/components/Object.tsx b/src/features/cseMachine/java/components/Object.tsx
index 25c06d19a4..73760e232f 100644
--- a/src/features/cseMachine/java/components/Object.tsx
+++ b/src/features/cseMachine/java/components/Object.tsx
@@ -1,4 +1,4 @@
-import { Object as JavaObject } from 'java-slang/dist/ec-evaluator/types';
+import { ECE } from 'java-slang';
 import React from 'react';
 import { Group } from 'react-konva';
 
@@ -9,7 +9,7 @@ import { Frame } from './Frame';
 export class Obj extends Visible {
   constructor(
     private readonly _frames: Frame[],
-    private readonly _object: JavaObject
+    private readonly _object: ECE.Object
   ) {
     super();
 
diff --git a/src/features/cseMachine/java/components/Stash.tsx b/src/features/cseMachine/java/components/Stash.tsx
index 79e3008be5..263fd097ac 100644
--- a/src/features/cseMachine/java/components/Stash.tsx
+++ b/src/features/cseMachine/java/components/Stash.tsx
@@ -1,5 +1,4 @@
-import { Stash as JavaStash } from 'java-slang/dist/ec-evaluator/components';
-import { StashItem as JavaStashItem, StructType } from 'java-slang/dist/ec-evaluator/types';
+import { ECE } from 'java-slang';
 import React from 'react';
 import { Group } from 'react-konva';
 
@@ -13,7 +12,7 @@ import { Variable } from './Variable';
 export class Stash extends Visible {
   private readonly _stashItems: StashItem[] = [];
 
-  constructor(stash: JavaStash) {
+  constructor(stash: ECE.Stash) {
     super();
 
     // Position.
@@ -46,26 +45,26 @@ export class Stash extends Visible {
     return <Group key={CseMachine.key++}>{this._stashItems.map(s => s.draw())}</Group>;
   }
 
-  private getStashItemString = (stashItem: JavaStashItem): string => {
+  private getStashItemString = (stashItem: ECE.StashItem): string => {
     switch (stashItem.kind) {
       case 'Literal':
         return stashItem.literalType.value;
-      case StructType.VARIABLE:
+      case ECE.StructType.VARIABLE:
         return 'location';
-      case StructType.TYPE:
+      case ECE.StructType.TYPE:
         return stashItem.type;
       default:
         return stashItem.kind.toLowerCase();
     }
   };
 
-  private getStashItemRef = (stashItem: JavaStashItem) => {
-    return stashItem.kind === StructType.CLOSURE
+  private getStashItemRef = (stashItem: ECE.StashItem) => {
+    return stashItem.kind === ECE.StructType.CLOSURE
       ? CseMachine.environment &&
           (CseMachine.environment.classes
             .flatMap(c => c.bindings)
             .find(b => b.value instanceof Method && b.value.method === stashItem)?.value as Method)
-      : stashItem.kind === StructType.VARIABLE
+      : stashItem.kind === ECE.StructType.VARIABLE
       ? CseMachine.environment &&
         ((CseMachine.environment.frames
           .flatMap(c => c.bindings)
@@ -79,10 +78,10 @@ export class Stash extends Visible {
             .flatMap(o => o.bindings)
             .find(b => b.value instanceof Variable && b.value.variable === stashItem)
             ?.value as Variable))
-      : stashItem.kind === StructType.CLASS
+      : stashItem.kind === ECE.StructType.CLASS
       ? CseMachine.environment &&
         CseMachine.environment.classes.find(c => c.frame === stashItem.frame)
-      : stashItem.kind === StructType.OBJECT
+      : stashItem.kind === ECE.StructType.OBJECT
       ? CseMachine.environment &&
         CseMachine.environment.objects.find(o => o.frame === stashItem.frame)
       : undefined;
diff --git a/src/features/cseMachine/java/components/Variable.tsx b/src/features/cseMachine/java/components/Variable.tsx
index 798cf0965f..380a7df816 100644
--- a/src/features/cseMachine/java/components/Variable.tsx
+++ b/src/features/cseMachine/java/components/Variable.tsx
@@ -1,4 +1,4 @@
-import { StructType, Variable as JavaVariable } from 'java-slang/dist/ec-evaluator/types';
+import { ECE } from 'java-slang';
 import React from 'react';
 import { Group, Rect } from 'react-konva';
 
@@ -33,7 +33,7 @@ export class Variable extends Visible {
   constructor(
     x: number,
     y: number,
-    private readonly _variable: JavaVariable
+    private readonly _variable: ECE.Variable
   ) {
     super();
 
@@ -51,7 +51,7 @@ export class Variable extends Visible {
         this._x + Config.TextPaddingX,
         this._y + this._type.height() + Config.TextPaddingX
       );
-    } else if (this.variable.value.kind === StructType.SYMBOL) {
+    } else if (this.variable.value.kind === ECE.StructType.SYMBOL) {
       this._value = new Text(
         '',
         this._x + Config.TextPaddingX,

From 8be3377d184233fd97c86d9483978eceb3e74da7 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 18:30:19 +0800
Subject: [PATCH 08/12] Fix lint

---
 .../cseMachine/java/components/Environment.tsx         | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/features/cseMachine/java/components/Environment.tsx b/src/features/cseMachine/java/components/Environment.tsx
index a66f963eb6..e578fb3a54 100644
--- a/src/features/cseMachine/java/components/Environment.tsx
+++ b/src/features/cseMachine/java/components/Environment.tsx
@@ -144,7 +144,10 @@ export class Environment extends Visible {
       .flatMap(obj => obj.frames)
       .forEach(of => {
         of.bindings.forEach(b => {
-          if (b.value instanceof Variable && b.value.variable.value.kind === ECE.StructType.VARIABLE) {
+          if (
+            b.value instanceof Variable &&
+            b.value.variable.value.kind === ECE.StructType.VARIABLE
+          ) {
             const variable = b.value.variable.value;
             const matchingVariable = this._classFrames
               .flatMap(c => c.bindings)
@@ -157,7 +160,10 @@ export class Environment extends Visible {
               matchingVariable.y() + matchingVariable.type.height()
             );
           }
-          if (b.value instanceof Variable && b.value.variable.value.kind === ECE.StructType.OBJECT) {
+          if (
+            b.value instanceof Variable &&
+            b.value.variable.value.kind === ECE.StructType.OBJECT
+          ) {
             const obj = b.value.variable.value.frame;
             const matchingObj = this._objects.find(o => o.getFrame().frame === obj)!;
             // Variable always has a box.

From 7934f6a602ea1c1e15a44ac509e301fb3b764ba1 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 18:54:42 +0800
Subject: [PATCH 09/12] Update snapshot

---
 .../__snapshots__/Playground.tsx.snap         | 24 ++++++++++---------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap b/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap
index e0ee8352d6..561f62ea1a 100644
--- a/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap
+++ b/src/pages/playground/__tests__/__snapshots__/Playground.tsx.snap
@@ -979,17 +979,19 @@ and also the
                               data-testid="cse-machine-default-text"
                               id="cse-machine-default-text"
                             >
-                              The CSE machine generates control, stash and environment model diagrams following a notation introduced in
-                               
-                              <a
-                                href="https://sourceacademy.org/sicpjs/3.2"
-                                rel="noopener noreferrer"
-                                target="_blank"
-                              >
-                                <i>
-                                  Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3, Section 2
-                                </i>
-                              </a>
+                              <span>
+                                The CSE machine generates control, stash and environment model diagrams following a notation introduced in
+                                 
+                                <a
+                                  href="https://sourceacademy.org/sicpjs/3.2"
+                                  rel="noopener noreferrer"
+                                  target="_blank"
+                                >
+                                  <i>
+                                    Structure and Interpretation of Computer Programs, JavaScript Edition, Chapter 3, Section 2
+                                  </i>
+                                </a>
+                              </span>
                               .
                               <br />
                               <br />

From b962fd02e5993195838f4382a5122c22c2479522 Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 19:07:04 +0800
Subject: [PATCH 10/12] Fix linting

---
 src/features/cseMachine/java/components/Environment.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/features/cseMachine/java/components/Environment.tsx b/src/features/cseMachine/java/components/Environment.tsx
index e578fb3a54..f8be475a31 100644
--- a/src/features/cseMachine/java/components/Environment.tsx
+++ b/src/features/cseMachine/java/components/Environment.tsx
@@ -90,7 +90,7 @@ export class Environment extends Visible {
     // Create class frames.
     const classFramesX = objectFramesX + objectFramesWidth + Config.FrameMinWidth;
     let classFramesY = this._y;
-    for (const [_, c] of environment.global.frame.entries()) {
+    for (const c of environment.global.frame.values()) {
       const classEnv = (c as ECE.Class).frame;
       const classFrameStroke =
         classEnv === environment.current ? Config.SA_CURRENT_ITEM : Config.SA_WHITE;

From edf485bea2039042644396f7869d69bd5a3d2c43 Mon Sep 17 00:00:00 2001
From: Liew Xin Yi <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 19:30:06 +0800
Subject: [PATCH 11/12] Fix typo

Co-authored-by: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
---
 src/commons/sideContent/content/SideContentCseMachine.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/commons/sideContent/content/SideContentCseMachine.tsx b/src/commons/sideContent/content/SideContentCseMachine.tsx
index 355f3c0cd5..fe6adf3048 100644
--- a/src/commons/sideContent/content/SideContentCseMachine.tsx
+++ b/src/commons/sideContent/content/SideContentCseMachine.tsx
@@ -447,7 +447,7 @@ class SideContentCseMachineBase extends React.Component<CseMachineProps, State>
     if (this.isJava()) {
       JavaCseMachine.zoomStage(isZoomIn, multiplier);
     } else {
-      Layout.zoomStage(false, 5);
+      Layout.zoomStage(isZoomIn, multiplier);
     }
   };
 

From 3d5fc872c919d8581fab97b3b4587f6dd107b64d Mon Sep 17 00:00:00 2001
From: xyliew25 <e0550381@u.nus.edu>
Date: Sat, 13 Apr 2024 19:36:41 +0800
Subject: [PATCH 12/12] Add TODO for err source node location

---
 src/commons/utils/JavaHelper.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/commons/utils/JavaHelper.ts b/src/commons/utils/JavaHelper.ts
index 493fcbfd81..fab36ed3c3 100644
--- a/src/commons/utils/JavaHelper.ts
+++ b/src/commons/utils/JavaHelper.ts
@@ -170,6 +170,7 @@ export async function runJavaCseMachine(code: string, targetStep: number, contex
   const convertJavaErrorToJsError = (e: ECE.SourceError): SourceError => ({
     type: ErrorType.RUNTIME,
     severity: ErrorSeverity.ERROR,
+    // TODO update err source node location once location info is avail
     location: {
       start: {
         line: 0,