Skip to content

Cache jiki objects #7611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -35,11 +35,11 @@ export default class BreakoutExercise extends Exercise {
this: Jiki.Instance,
executionCtx: ExecutionContext
) {
this.fields['cx'] = new Jiki.Number(50)
this.fields['cy'] = new Jiki.Number(100 - Ball['default_radius'])
this.fields['radius'] = new Jiki.Number(Ball['default_radius'])
this.fields['y_velocity'] = new Jiki.Number(-1)
this.fields['x_velocity'] = new Jiki.Number(-1)
this.fields['cx'] = Jiki.Number.fetch(50)
this.fields['cy'] = Jiki.Number.fetch(100 - Ball['default_radius'])
this.fields['radius'] = Jiki.Number.fetch(Ball['default_radius'])
this.fields['y_velocity'] = Jiki.Number.fetch(-1)
this.fields['x_velocity'] = Jiki.Number.fetch(-1)
createBall(executionCtx, this)
})
Ball.addGetter('cx')
@@ -129,9 +129,9 @@ export default class BreakoutExercise extends Exercise {
) {
this.fields['left'] = left
this.fields['top'] = top
this.fields['width'] = new Jiki.Number(16)
this.fields['height'] = new Jiki.Number(Block['default_height'])
this.fields['smashed'] = new Jiki.Boolean(false)
this.fields['width'] = Jiki.Number.fetch(16)
this.fields['height'] = Jiki.Number.fetch(Block['default_height'])
this.fields['smashed'] = Jiki.Boolean.fetch(false)
createBlock(executionCtx, this as BlockInstance)
})
Block.addGetter('top')
@@ -225,8 +225,8 @@ export default class BreakoutExercise extends Exercise {
const newCx = cx + x_velocity
const newCy = cy + y_velocity

ball.setField('cx', new Jiki.Number(newCx))
ball.setField('cy', new Jiki.Number(newCy))
ball.setField('cx', Jiki.Number.fetch(newCx))
ball.setField('cy', Jiki.Number.fetch(newCy))

this.ballPositions.push([newCx, newCy])

39 changes: 18 additions & 21 deletions app/javascript/interpreter/executor.ts
Original file line number Diff line number Diff line change
@@ -102,6 +102,8 @@ import { executeMethodCallExpression } from './executor/executeMethodCallExpress
import { executeInstantiationExpression } from './executor/executeInstantiationExpression'
import { executeGetterExpression } from './executor/executeGetterExpression'

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>

export type ExecutionContext = {
state: Record<string, any>
getCurrentTime: Function
@@ -832,7 +834,7 @@ export class Executor {
// Because we're using keys that can be strings here,
// guard in case we need to wrap them as Jiki strings!
if (typeof temporaryVariableValue == 'string') {
temporaryVariableValue = new Jiki.String(temporaryVariableValue)
temporaryVariableValue = Jiki.String.fetch(temporaryVariableValue)
}
const temporaryVariableName = statement.elementName.lexeme
temporaryVariableNames.push(temporaryVariableName)
@@ -882,7 +884,7 @@ export class Executor {
counterVariableName: string | null
): void {
if (counterVariableName) {
this.environment.define(counterVariableName, new Jiki.Number(iteration))
this.environment.define(counterVariableName, Jiki.Number.fetch(iteration))
}

try {
@@ -948,10 +950,6 @@ export class Executor {
iteration++
this.guardInfiniteLoop(statement.keyword.location)

if (counterVariableName) {
this.environment.define(counterVariableName, iteration)
}

this.executeFrame<EvaluationResultRepeatStatement>(statement, () => {
return {
type: 'RepeatStatement',
@@ -1054,11 +1052,11 @@ export class Executor {
): EvaluationResultLiteralExpression {
let jikiObject
if (isBoolean(expression.value)) {
jikiObject = new Jiki.Boolean(expression.value)
jikiObject = Jiki.Boolean.fetch(expression.value)
} else if (isNumber(expression.value)) {
jikiObject = new Jiki.Number(expression.value)
jikiObject = Jiki.Number.fetch(expression.value)
} else if (isString(expression.value)) {
jikiObject = new Jiki.String(expression.value)
jikiObject = Jiki.String.fetch(expression.value)
} else {
// If this happens, we've gone really wrong somewhere!
this.error('InvalidLiteralType', expression.location, {
@@ -1094,7 +1092,7 @@ export class Executor {
function: value,
// This is needed so that the null guard doesn't
// blow up upstream
jikiObject: new Jiki.Boolean(true),
jikiObject: Jiki.Boolean.fetch(true),
}
}

@@ -1107,7 +1105,7 @@ export class Executor {
type: 'ClassLookupExpression',
name: expression.name.lexeme,
class: klass,
jikiObject: new Jiki.Boolean(true),
jikiObject: Jiki.Boolean.fetch(true),
}
}

@@ -1121,14 +1119,14 @@ export class Executor {
this.verifyBoolean(operand.jikiObject, expression.operand)
return {
type: 'UnaryExpression',
jikiObject: new Jiki.Boolean(!operand.jikiObject.value),
jikiObject: Jiki.Boolean.fetch(!operand.jikiObject.value),
right: operand,
}
case 'MINUS':
this.verifyNumber(operand.jikiObject, expression.operand)
return {
type: 'UnaryExpression',
jikiObject: new Jiki.Number(-operand.jikiObject.value),
jikiObject: Jiki.Number.fetch(-operand.jikiObject.value),
right: operand,
}
}
@@ -1159,7 +1157,7 @@ export class Executor {
this.verifyBoolean(rightOr.jikiObject, expression.right)
}

const jikiObject = new Jiki.Boolean(
const jikiObject = Jiki.Boolean.fetch(
leftOr.jikiObject.value || rightOr?.jikiObject.value
)
return {
@@ -1181,7 +1179,7 @@ export class Executor {
this.verifyBoolean(rightAnd.jikiObject, expression.right)
}

const jikiObject = new Jiki.Boolean(
const jikiObject = Jiki.Boolean.fetch(
leftAnd.jikiObject.value && rightAnd?.jikiObject.value
)

@@ -1310,7 +1308,7 @@ export class Executor {

// Extra using 0-index
// Then wrap the new object
const value = new Jiki.String(
const value = Jiki.String.fetch(
obj.jikiObject.value[idx.jikiObject.value - 1]
)

@@ -1644,7 +1642,7 @@ export class Executor {
): void {
if (location == null) location = Location.unknown

const frame: Frame = {
const frame: Optional<Frame, 'description'> = {
code: location.toCode(this.sourceCode),
line: location.line,
status,
@@ -1653,16 +1651,15 @@ export class Executor {
time: this.time,
// Multiple the time by 100 and floor it to get an integer
timelineTime: Math.round(this.time * 100),
description: '',
context: context,
}
if (process.env.NODE_ENV == 'test') {
frame.variables = cloneDeep(this.environment.variables())
frame.variables = Jiki.unwrapJikiObject(this.environment.variables())
}
frame.description = describeFrame(frame, {
frame.description = describeFrame(frame as Frame, {
functionDescriptions: this.externalFunctionDescriptions,
})
this.frames.push(frame)
this.frames.push(frame as Frame)

this.time += this.timePerFrame
}
36 changes: 16 additions & 20 deletions app/javascript/interpreter/executor/executeBinaryExpression.ts
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import {
} from '../evaluation-result'
import { Executor } from '../executor'
import { BinaryExpression } from '../expression'
import * as JikiTypes from '../jikiObjects'
import * as Jiki from '../jikiObjects'

const DP_MULTIPLE = 100000

@@ -75,7 +75,7 @@ function handle_inequality(
leftResult: EvaluationResultExpression,
rightResult: EvaluationResultExpression
): any {
return new JikiTypes.Boolean(
return Jiki.Boolean.fetch(
leftResult.jikiObject.value !== rightResult.jikiObject.value
)
}
@@ -86,7 +86,7 @@ function handle_equality(
leftResult: EvaluationResultExpression,
rightResult: EvaluationResultExpression
): any {
return new JikiTypes.Boolean(
return Jiki.Boolean.fetch(
leftResult.jikiObject.value === rightResult.jikiObject.value
)
}
@@ -99,7 +99,7 @@ function handle_greater(
): any {
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
return new JikiTypes.Boolean(
return Jiki.Boolean.fetch(
leftResult.jikiObject.value > rightResult.jikiObject.value
)
}
@@ -112,7 +112,7 @@ function handle_greater_equal(
): any {
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
return new JikiTypes.Boolean(
return Jiki.Boolean.fetch(
leftResult.jikiObject.value >= rightResult.jikiObject.value
)
}
@@ -125,7 +125,7 @@ function handle_less(
): any {
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
return new JikiTypes.Boolean(
return Jiki.Boolean.fetch(
leftResult.jikiObject.value < rightResult.jikiObject.value
)
}
@@ -138,7 +138,7 @@ function handle_less_equal(
): any {
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
return new JikiTypes.Boolean(
return Jiki.Boolean.fetch(
leftResult.jikiObject.value <= rightResult.jikiObject.value
)
}
@@ -152,9 +152,7 @@ function handle_minus(
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
const minusValue = leftResult.jikiObject.value - rightResult.jikiObject.value
return new JikiTypes.Number(
Math.round(minusValue * DP_MULTIPLE) / DP_MULTIPLE
)
return Jiki.Number.fetch(Math.round(minusValue * DP_MULTIPLE) / DP_MULTIPLE)
}

function handle_plus(
@@ -166,7 +164,7 @@ function handle_plus(
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
const plusValue = leftResult.jikiObject.value + rightResult.jikiObject.value
return new JikiTypes.Number(Math.round(plusValue * DP_MULTIPLE) / DP_MULTIPLE)
return Jiki.Number.fetch(Math.round(plusValue * DP_MULTIPLE) / DP_MULTIPLE)
}

function handle_slash(
@@ -178,9 +176,7 @@ function handle_slash(
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
const slashValue = leftResult.jikiObject.value / rightResult.jikiObject.value
return new JikiTypes.Number(
Math.round(slashValue * DP_MULTIPLE) / DP_MULTIPLE
)
return Jiki.Number.fetch(Math.round(slashValue * DP_MULTIPLE) / DP_MULTIPLE)
}

function handle_star(
@@ -192,7 +188,7 @@ function handle_star(
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
const starValue = leftResult.jikiObject.value * rightResult.jikiObject.value
return new JikiTypes.Number(Math.round(starValue * DP_MULTIPLE) / DP_MULTIPLE)
return Jiki.Number.fetch(Math.round(starValue * DP_MULTIPLE) / DP_MULTIPLE)
}

function handle_percent(
@@ -203,7 +199,7 @@ function handle_percent(
): any {
executor.verifyNumber(leftResult.jikiObject, expression.left)
executor.verifyNumber(rightResult.jikiObject, expression.right)
return new JikiTypes.Number(
return Jiki.Number.fetch(
leftResult.jikiObject.value % rightResult.jikiObject.value
)
}
@@ -215,8 +211,8 @@ function guardLists(
rightResult: EvaluationResultExpression
) {
if (
leftResult.jikiObject instanceof JikiTypes.List &&
rightResult.jikiObject instanceof JikiTypes.List
leftResult.jikiObject instanceof Jiki.List &&
rightResult.jikiObject instanceof Jiki.List
) {
executor.error('ListsCannotBeCompared', expression.location)
}
@@ -229,8 +225,8 @@ function guardObjects(
rightResult: EvaluationResultExpression
) {
if (
leftResult.jikiObject instanceof JikiTypes.Instance ||
rightResult.jikiObject instanceof JikiTypes.Instance
leftResult.jikiObject instanceof Jiki.Instance ||
rightResult.jikiObject instanceof Jiki.Instance
) {
executor.error('ObjectsCannotBeCompared', expression.location)
}
3 changes: 1 addition & 2 deletions app/javascript/interpreter/frames.ts
Original file line number Diff line number Diff line change
@@ -47,8 +47,7 @@ export type Frame = {
code: string
status: FrameExecutionStatus
error?: RuntimeError
priorVariables: Record<string, any>
variables: Record<string, any>
variables?: Record<string, any>
time: number
timelineTime: number
result?: EvaluationResult
19 changes: 19 additions & 0 deletions app/javascript/interpreter/jikiObjects.ts
Original file line number Diff line number Diff line change
@@ -174,21 +174,37 @@ export abstract class Literal extends Primitive {
}

export class Number extends Literal {
static cache = {}
constructor(value: number) {
super('number', value)
}
public toArg(): Number {
return new Number(this.value)
}
public static fetch(value: number): Number {
const obj = this.cache[value]
if (obj) return obj
const newObj = new Number(value)
this.cache[value] = newObj
return newObj
}
}

export class String extends Literal {
static cache: Map<string, String> = new Map()
constructor(value: string) {
super('string', value)
}
public toArg(): String {
return new String(this.value)
}
public static fetch(value: string): String {
const obj = this.cache.get(value)
if (obj) return obj
const newObj = new String(value)
this.cache.set(value, newObj)
return newObj
}
}

export class Boolean extends Literal {
@@ -198,6 +214,9 @@ export class Boolean extends Literal {
public toArg(): Boolean {
return new Boolean(this.value)
}
public static fetch(val: boolean): Boolean {
return val ? True : False
}
}
export const True = new Boolean(true)
export const False = new Boolean(false)
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.