diff --git a/src/app/(withAuth)/courses/[courseId]/lectures/[lectureId]/problems/[problemId]/ProblmBody.tsx b/src/app/(withAuth)/courses/[courseId]/lectures/[lectureId]/problems/[problemId]/ProblmBody.tsx index 0704130e..3915f8f0 100644 --- a/src/app/(withAuth)/courses/[courseId]/lectures/[lectureId]/problems/[problemId]/ProblmBody.tsx +++ b/src/app/(withAuth)/courses/[courseId]/lectures/[lectureId]/problems/[problemId]/ProblmBody.tsx @@ -316,35 +316,36 @@ function getInitialVariables( ): Record { let adjustedPreviousTraceItemIndex = previousTraceItemIndex; + let nonGlobalVariableShouldBeEmpty = false; if (problemType === 'step') { const currentDepth = traceItems[currentTraceItemIndex].depth; while (adjustedPreviousTraceItemIndex > 0 && currentDepth !== traceItems[adjustedPreviousTraceItemIndex].depth) { if (currentDepth > traceItems[adjustedPreviousTraceItemIndex].depth) { // 過去のトレースの方が現在のトレースよりも深いため。 - return getEmptyVariables(currentVariables); + nonGlobalVariableShouldBeEmpty = true; + break; } adjustedPreviousTraceItemIndex--; } - - if ( - traceItems[currentTraceItemIndex].callStack.at(-1) !== traceItems[adjustedPreviousTraceItemIndex].callStack.at(-1) - ) { - // 過去のトレースと現在のトレースのスタックトレースが別であるため。 - return getEmptyVariables(currentVariables); - } + // 過去のトレースと現在のトレースのスタックトレースが別であるかどうか。 + nonGlobalVariableShouldBeEmpty ||= + traceItems[currentTraceItemIndex].callStack.at(-1) !== + traceItems[adjustedPreviousTraceItemIndex].callStack.at(-1); } return Object.fromEntries( Object.entries(currentVariables) .filter(([_, value]) => typeof value === 'number' || typeof value === 'string') - .map(([key]) => [key, traceItems[adjustedPreviousTraceItemIndex].vars[key]?.toString() ?? '']) + .map(([key]) => { + const isGlobalVariable = isUpperCase(key.slice(0, 1)); + console.log(key, isGlobalVariable); + if (isGlobalVariable) return [key, traceItems[previousTraceItemIndex].vars[key]?.toString() ?? '']; + if (nonGlobalVariableShouldBeEmpty) return [key, '']; + return [key, traceItems[adjustedPreviousTraceItemIndex].vars[key]?.toString() ?? '']; + }) ); } -function getEmptyVariables(currentVariables: TraceItemVariable): Record { - return Object.fromEntries( - Object.entries(currentVariables) - .filter(([_, value]) => typeof value === 'number' || typeof value === 'string') - .map(([key]) => [key, '']) - ); +function isUpperCase(str: string): boolean { + return str === str.toUpperCase() && str !== str.toLowerCase(); } diff --git a/src/problems/problemData.ts b/src/problems/problemData.ts index 3bf6cc6e..2f7e36c5 100644 --- a/src/problems/problemData.ts +++ b/src/problems/problemData.ts @@ -118,6 +118,7 @@ export const problemIds = [ 'test3', 'test4', 'test5', + 'test9', ] as const; export type ProblemId = (typeof problemIds)[number]; @@ -246,6 +247,7 @@ export const problemIdToName: Record = { test3: 'ステップ実行のテスト用問題(3)', test4: 'ステップ実行のテスト用問題(4)', test5: 'チェックポイント取得のテスト用問題', + test9: 'ステップ実行のテスト用問題(9)', }; export const courseIdToLectureIndexToProblemIds: Record = { @@ -303,7 +305,7 @@ export const courseIdToLectureIndexToProblemIds: Record // 第8回 ['oop1'], ], - test: [['test1', 'test2', 'test3', 'test4', 'test5', 'oop1', 'oop2', 'garbageCollection1', 'polymorphism1']], + test: [['test1', 'test2', 'test3', 'test4', 'test5', 'test9', 'oop1', 'oop2', 'garbageCollection1', 'polymorphism1']], }; export const courseIdToLectureIds: Record = JSON.parse( @@ -3678,6 +3680,100 @@ public class Straight { c.forward(); // step } } +`, + }, + test9: { + // Javaの静的フィールド(つまり、グローバル変数)を扱う場合、 `myGlobal` を使うこと。 + // 静的メソッドは普通の関数で代替すること。 + // 独自クラスを定義するコードでは `main()` 関数を定義すること。 + instrumented: ` +myGlobal.Settings = { speed: 0 }; + +function main() { + const t1 = call(MyTurtle)(); + myGlobal.Settings.speed = 2; // step + call(t1.moveForward.bind(t1))(); + + const t2 = call(MyTurtle2, 'x', 'y')(2, 2); + call(increaseSpeed)(); + call(t1.moveForward.bind(t1))(); + call(t2.moveForward.bind(t2))(); +} + +function increaseSpeed() { + myGlobal.Settings.speed++; // step +} + +class MyTurtle { + constructor() { + this.t = new Turtle(); // step + } + + moveForward() { + for (s.set('i', 0); s.get('i') < myGlobal.Settings.speed; s.set('i', s.get('i') + 1)) { // step + this.t.前に進む(); // step + } + } +} + +class MyTurtle2 { + constructor(x, y) { + this.t = new Turtle(x, y); // step + } + + moveForward() { + for (s.set('i', 0); s.get('i') < myGlobal.Settings.speed; s.set('i', s.get('i') + 1)) { // step + this.t.前に進む(); // step + } + } +} + +main(); +`, + java: ` +public class Main { + public static void main(String[] args) { + MyTurtle t1 = new MyTurtle(); // caller + Settings.speed = 2; // step + t1.moveForward(); // caller + + MyTurtle t2 = new MyTurtle2(2, 2); // caller + Settings.increaseSpeed(); // caller + t1.moveForward(); // caller + t2.moveForward(); // caller + } +} + +class Settings { + static public int speed; + + public increaseSpeed() { + speed++; // step + } +} + +class MyTurtle { + private Turtle t = new Turtle(); // step + + void moveForward(Turtle t) { + for (int i = 0; i < Settings.speed; i++) { // step + t.前に進む(); // step + } + } +} + +class MyTurtle2 { + private Turtle t; + + MyTurtle2(int x, int y) { + t = new Turtle(x, y); // step + } + void moveForward(Turtle t) { + for (int i = 0; i < Settings.speed; i++) { // step + t.前に進む(); // step + } + } +} `, }, };