From d62492f307f739006aa8d8f31822d41d04790e54 Mon Sep 17 00:00:00 2001 From: tohlh Date: Tue, 25 Mar 2025 19:34:00 +0800 Subject: [PATCH 1/5] Add support for language options --- src/commons/assessment/AssessmentTypes.ts | 1 + .../WorkspaceSaga/helpers/clearContext.ts | 13 ++++++----- src/commons/utils/JsSlangHelper.ts | 22 ++++++++++++++++--- src/commons/workspace/WorkspaceReducer.ts | 4 +++- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/commons/assessment/AssessmentTypes.ts b/src/commons/assessment/AssessmentTypes.ts index c3d5cecbf4..7ea89ac85c 100644 --- a/src/commons/assessment/AssessmentTypes.ts +++ b/src/commons/assessment/AssessmentTypes.ts @@ -175,6 +175,7 @@ export type Library = { 2?: string; // For mission control }>; moduleParams?: any; + languageOptions?: any; }; export type Testcase = { diff --git a/src/commons/sagas/WorkspaceSaga/helpers/clearContext.ts b/src/commons/sagas/WorkspaceSaga/helpers/clearContext.ts index a12b6f3100..e1fabb6c13 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/clearContext.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/clearContext.ts @@ -1,6 +1,6 @@ import { Context } from 'js-slang'; import { defineSymbol } from 'js-slang/dist/createContext'; -import { Variant } from 'js-slang/dist/types'; +import { LanguageOptions, Variant } from 'js-slang/dist/types'; import { put, select, take } from 'redux-saga/effects'; import WorkspaceActions from 'src/commons/workspace/WorkspaceActions'; @@ -10,18 +10,20 @@ import { actions } from '../../../utils/ActionsHelper'; import { WorkspaceLocation } from '../../../workspace/WorkspaceTypes'; export function* clearContext(workspaceLocation: WorkspaceLocation, entrypointCode: string) { - const [chapter, symbols, externalLibraryName, globals, variant]: [ + const [chapter, symbols, externalLibraryName, globals, variant, languageOptions]: [ number, string[], ExternalLibraryName, Array<[string, any]>, - Variant + Variant, + LanguageOptions ] = yield select((state: OverallState) => [ state.workspaces[workspaceLocation].context.chapter, state.workspaces[workspaceLocation].context.externalSymbols, state.workspaces[workspaceLocation].externalLibrary, state.workspaces[workspaceLocation].globals, - state.workspaces[workspaceLocation].context.variant + state.workspaces[workspaceLocation].context.variant, + state.workspaces[workspaceLocation].context.languageOptions ]); const library = { @@ -31,7 +33,8 @@ export function* clearContext(workspaceLocation: WorkspaceLocation, entrypointCo name: externalLibraryName, symbols }, - globals + globals, + languageOptions }; // Clear the context, with the same chapter and externalSymbols as before. diff --git a/src/commons/utils/JsSlangHelper.ts b/src/commons/utils/JsSlangHelper.ts index ab88e89db8..a67105583c 100644 --- a/src/commons/utils/JsSlangHelper.ts +++ b/src/commons/utils/JsSlangHelper.ts @@ -1,6 +1,13 @@ /* tslint:disable: ban-types*/ import createSlangContext, { defineBuiltin, importBuiltins } from 'js-slang/dist/createContext'; -import { Chapter, Context, CustomBuiltIns, Value, Variant } from 'js-slang/dist/types'; +import { + Chapter, + Context, + CustomBuiltIns, + LanguageOptions, + Value, + Variant +} from 'js-slang/dist/types'; import { stringify } from 'js-slang/dist/utils/stringify'; import { difference, keys } from 'lodash'; import CseMachine from 'src/features/cseMachine/CseMachine'; @@ -148,9 +155,17 @@ export function createContext( chapter: Chapter, externals: string[], externalContext: T, - variant: Variant = Variant.DEFAULT + variant: Variant = Variant.DEFAULT, + languageOptions?: LanguageOptions ) { - return createSlangContext(chapter, variant, externals, externalContext, externalBuiltIns); + return createSlangContext( + chapter, + variant, + languageOptions, + externals, + externalContext, + externalBuiltIns + ); } // Assumes that the grader doesn't need additional external libraries apart from the standard @@ -166,6 +181,7 @@ function loadStandardLibraries(proxyContext: Context, customBuiltIns: CustomBuil // intercepts reads from the underlying Context and returns desired values export function makeElevatedContext(context: Context) { function ProxyFrame() {} + ProxyFrame.prototype = context.runtime.environments[0].head; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index a4cc98a8c3..10bd5a28d5 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -90,6 +90,7 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { const workspaceLocation = getWorkspaceLocation(action); // For some reason mutating the state directly results in type // errors, so we have to do it the old-fashioned way + console.log('endClearContext', action.payload.library.languageOptions); return { ...state, [workspaceLocation]: { @@ -98,7 +99,8 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { action.payload.library.chapter, action.payload.library.external.symbols, workspaceLocation, - action.payload.library.variant + action.payload.library.variant, + action.payload.library.languageOptions ), globals: action.payload.library.globals, externalLibrary: action.payload.library.external.name From 9dd4a7df85edfb6dbbf84098878a8be2702c76b0 Mon Sep 17 00:00:00 2001 From: tohlh Date: Tue, 25 Mar 2025 21:13:10 +0800 Subject: [PATCH 2/5] Fix issues with prepend code on typed variant --- .../helpers/blockExtraMethods.ts | 7 ++++-- .../sagas/WorkspaceSaga/helpers/evalEditor.ts | 25 ++++++++++++------- src/commons/utils/JsSlangHelper.ts | 8 ++++++ src/commons/workspace/WorkspaceReducer.ts | 1 - 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts b/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts index 95ec7e6fb7..6ddcb1a38a 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts @@ -2,12 +2,13 @@ import { Context } from 'js-slang'; import { call } from 'redux-saga/effects'; import { - getBlockExtraMethodsString, + getBlockExtraMethodsString, getBlockExtraMethodsStringTypedVariant, getDifferenceInMethods, getStoreExtraMethodsString } from '../../../utils/JsSlangHelper'; import { EVAL_SILENT, WorkspaceLocation } from '../../../workspace/WorkspaceTypes'; import { evalCodeSaga } from './evalCode'; +import { Variant } from 'js-slang/dist/types'; export function* blockExtraMethods( elevatedContext: Context, @@ -35,7 +36,9 @@ export function* blockExtraMethods( ); } - const nullifier = getBlockExtraMethodsString(toBeBlocked); + const nullifier = context.variant === Variant.TYPED + ? getBlockExtraMethodsStringTypedVariant(toBeBlocked) + : getBlockExtraMethodsString(toBeBlocked); const nullifierFilePath = '/nullifier.js'; const nullifierFiles = { [nullifierFilePath]: nullifier diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts index 4e7cb11ca2..cf4e8bcb6a 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts @@ -14,6 +14,7 @@ import { blockExtraMethods } from './blockExtraMethods'; import { clearContext } from './clearContext'; import { evalCodeSaga } from './evalCode'; import { insertDebuggerStatements } from './insertDebuggerStatements'; +import { Variant } from 'js-slang/dist/types'; export function* evalEditorSaga( workspaceLocation: WorkspaceLocation @@ -93,19 +94,25 @@ export function* evalEditorSaga( const prependFiles = { [prependFilePath]: prepend }; - yield call( - evalCodeSaga, - prependFiles, - prependFilePath, - elevatedContext, - execTime, - workspaceLocation, - EVAL_SILENT - ); + if (context.variant !== Variant.TYPED) { + yield call( + evalCodeSaga, + prependFiles, + prependFilePath, + elevatedContext, + execTime, + workspaceLocation, + EVAL_SILENT, + ); + } + // Block use of methods from privileged context yield* blockExtraMethods(elevatedContext, context, execTime, workspaceLocation); } + if (context.variant === Variant.TYPED) { + files[entrypointFilePath] = prepend + files[entrypointFilePath]; + } yield call( evalCodeSaga, files, diff --git a/src/commons/utils/JsSlangHelper.ts b/src/commons/utils/JsSlangHelper.ts index a67105583c..5c51fe94e4 100644 --- a/src/commons/utils/JsSlangHelper.ts +++ b/src/commons/utils/JsSlangHelper.ts @@ -259,3 +259,11 @@ export function getBlockExtraMethodsString(toRemove: string[]) { ) .join('\n'); } + +export function getBlockExtraMethodsStringTypedVariant(toRemove: string[]) { + return toRemove + .map(x => + x === 'makeUndefinedErrorFunction' ? '' : `const ${x} : string = makeUndefinedErrorFunction('${x}');` + ) + .join('\n'); +} diff --git a/src/commons/workspace/WorkspaceReducer.ts b/src/commons/workspace/WorkspaceReducer.ts index 10bd5a28d5..57f6e557d0 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -90,7 +90,6 @@ const newWorkspaceReducer = createReducer(defaultWorkspaceManager, builder => { const workspaceLocation = getWorkspaceLocation(action); // For some reason mutating the state directly results in type // errors, so we have to do it the old-fashioned way - console.log('endClearContext', action.payload.library.languageOptions); return { ...state, [workspaceLocation]: { From f4e162d26d5ab5255e09e21d42fd626a87e8cb7e Mon Sep 17 00:00:00 2001 From: tohlh Date: Wed, 16 Apr 2025 11:54:03 +0800 Subject: [PATCH 3/5] Fix line number issues --- src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts index cf4e8bcb6a..f6d6096e5a 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts @@ -111,7 +111,10 @@ export function* evalEditorSaga( } if (context.variant === Variant.TYPED) { - files[entrypointFilePath] = prepend + files[entrypointFilePath]; + // Prepend was multi-line, now we need to split them by \n and join them + // This is to avoid extra lines in the editor which affects the error message location + const prependSingleLine = prepend.split('\n').join(''); + files[entrypointFilePath] = prependSingleLine + files[entrypointFilePath]; } yield call( evalCodeSaga, From f37927730ffa02989b8215530a5c1a3046c71c9c Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Fri, 18 Apr 2025 03:27:31 +0800 Subject: [PATCH 4/5] Fix format and lint --- .../sagas/WorkspaceSaga/helpers/blockExtraMethods.ts | 12 +++++++----- .../sagas/WorkspaceSaga/helpers/evalEditor.ts | 4 ++-- src/commons/utils/JsSlangHelper.ts | 4 +++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts b/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts index 6ddcb1a38a..a3b0f2c11f 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts @@ -1,14 +1,15 @@ import { Context } from 'js-slang'; +import { Variant } from 'js-slang/dist/types'; import { call } from 'redux-saga/effects'; import { - getBlockExtraMethodsString, getBlockExtraMethodsStringTypedVariant, + getBlockExtraMethodsString, + getBlockExtraMethodsStringTypedVariant, getDifferenceInMethods, getStoreExtraMethodsString } from '../../../utils/JsSlangHelper'; import { EVAL_SILENT, WorkspaceLocation } from '../../../workspace/WorkspaceTypes'; import { evalCodeSaga } from './evalCode'; -import { Variant } from 'js-slang/dist/types'; export function* blockExtraMethods( elevatedContext: Context, @@ -36,9 +37,10 @@ export function* blockExtraMethods( ); } - const nullifier = context.variant === Variant.TYPED - ? getBlockExtraMethodsStringTypedVariant(toBeBlocked) - : getBlockExtraMethodsString(toBeBlocked); + const nullifier = + context.variant === Variant.TYPED + ? getBlockExtraMethodsStringTypedVariant(toBeBlocked) + : getBlockExtraMethodsString(toBeBlocked); const nullifierFilePath = '/nullifier.js'; const nullifierFiles = { [nullifierFilePath]: nullifier diff --git a/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts index cf4e8bcb6a..abf4e6fade 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts @@ -1,4 +1,5 @@ import { FSModule } from 'browserfs/dist/node/core/FS'; +import { Variant } from 'js-slang/dist/types'; import { call, put, select, StrictEffect } from 'redux-saga/effects'; import WorkspaceActions from 'src/commons/workspace/WorkspaceActions'; @@ -14,7 +15,6 @@ import { blockExtraMethods } from './blockExtraMethods'; import { clearContext } from './clearContext'; import { evalCodeSaga } from './evalCode'; import { insertDebuggerStatements } from './insertDebuggerStatements'; -import { Variant } from 'js-slang/dist/types'; export function* evalEditorSaga( workspaceLocation: WorkspaceLocation @@ -102,7 +102,7 @@ export function* evalEditorSaga( elevatedContext, execTime, workspaceLocation, - EVAL_SILENT, + EVAL_SILENT ); } diff --git a/src/commons/utils/JsSlangHelper.ts b/src/commons/utils/JsSlangHelper.ts index 5c51fe94e4..d6c92eaeef 100644 --- a/src/commons/utils/JsSlangHelper.ts +++ b/src/commons/utils/JsSlangHelper.ts @@ -263,7 +263,9 @@ export function getBlockExtraMethodsString(toRemove: string[]) { export function getBlockExtraMethodsStringTypedVariant(toRemove: string[]) { return toRemove .map(x => - x === 'makeUndefinedErrorFunction' ? '' : `const ${x} : string = makeUndefinedErrorFunction('${x}');` + x === 'makeUndefinedErrorFunction' + ? '' + : `const ${x} : string = makeUndefinedErrorFunction('${x}');` ) .join('\n'); } From 94c2b7223126040301fd67e6fb70b03e67abc865 Mon Sep 17 00:00:00 2001 From: tohlh Date: Fri, 18 Apr 2025 11:41:55 +0800 Subject: [PATCH 5/5] Fix languageOptions field type --- src/commons/assessment/AssessmentTypes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commons/assessment/AssessmentTypes.ts b/src/commons/assessment/AssessmentTypes.ts index 7ea89ac85c..8d73a922b6 100644 --- a/src/commons/assessment/AssessmentTypes.ts +++ b/src/commons/assessment/AssessmentTypes.ts @@ -1,4 +1,4 @@ -import { Chapter, SourceError, Variant } from 'js-slang/dist/types'; +import { Chapter, LanguageOptions, SourceError, Variant } from 'js-slang/dist/types'; import { ExternalLibrary, ExternalLibraryName } from '../application/types/ExternalTypes'; @@ -175,7 +175,7 @@ export type Library = { 2?: string; // For mission control }>; moduleParams?: any; - languageOptions?: any; + languageOptions?: LanguageOptions; }; export type Testcase = {