diff --git a/src/commons/assessment/AssessmentTypes.ts b/src/commons/assessment/AssessmentTypes.ts index 2bd726f9b3..02c1b806ae 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'; @@ -177,6 +177,7 @@ export type Library = { 2?: string; // For mission control }>; moduleParams?: any; + languageOptions?: LanguageOptions; }; export type Testcase = { diff --git a/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts b/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts index 95ec7e6fb7..a3b0f2c11f 100644 --- a/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts +++ b/src/commons/sagas/WorkspaceSaga/helpers/blockExtraMethods.ts @@ -1,8 +1,10 @@ import { Context } from 'js-slang'; +import { Variant } from 'js-slang/dist/types'; import { call } from 'redux-saga/effects'; import { getBlockExtraMethodsString, + getBlockExtraMethodsStringTypedVariant, getDifferenceInMethods, getStoreExtraMethodsString } from '../../../utils/JsSlangHelper'; @@ -35,7 +37,10 @@ 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/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/sagas/WorkspaceSaga/helpers/evalEditor.ts b/src/commons/sagas/WorkspaceSaga/helpers/evalEditor.ts index 4e7cb11ca2..225805e51a 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'; @@ -93,19 +94,28 @@ 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) { + // 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, files, diff --git a/src/commons/utils/JsSlangHelper.ts b/src/commons/utils/JsSlangHelper.ts index ab88e89db8..d6c92eaeef 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 @@ -243,3 +259,13 @@ 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 a4cc98a8c3..57f6e557d0 100644 --- a/src/commons/workspace/WorkspaceReducer.ts +++ b/src/commons/workspace/WorkspaceReducer.ts @@ -98,7 +98,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