From 24cb18d751e8043cbfdce72f91349f365d2d9f95 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:20:57 +0300 Subject: [PATCH 01/38] Add CI workflow for cn1playground language smoke tests --- .github/workflows/cn1playground-language.yml | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/cn1playground-language.yml diff --git a/.github/workflows/cn1playground-language.yml b/.github/workflows/cn1playground-language.yml new file mode 100644 index 0000000000..bf6c59addf --- /dev/null +++ b/.github/workflows/cn1playground-language.yml @@ -0,0 +1,39 @@ +name: CN1 Playground Language Tests + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - 'scripts/cn1playground/**' + - '.github/workflows/cn1playground-language.yml' + push: + branches: [main, master] + paths: + - 'scripts/cn1playground/**' + - '.github/workflows/cn1playground-language.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + language-smoke: + name: Playground language smoke + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Java 8 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '8' + cache: maven + + - name: Run playground language smoke tests + run: | + set -euo pipefail + cd scripts/cn1playground + tools/run-playground-smoke-tests.sh From da4aed416f6397668c485252d652ff0c58ab53bb Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:27:13 +0300 Subject: [PATCH 02/38] Fix playground workflow script invocation permissions --- .github/workflows/cn1playground-language.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cn1playground-language.yml b/.github/workflows/cn1playground-language.yml index bf6c59addf..e940c8fbc7 100644 --- a/.github/workflows/cn1playground-language.yml +++ b/.github/workflows/cn1playground-language.yml @@ -36,4 +36,4 @@ jobs: run: | set -euo pipefail cd scripts/cn1playground - tools/run-playground-smoke-tests.sh + bash tools/run-playground-smoke-tests.sh From 3cd3e096b6247b0976fe9b28a5528e60588c5b2a Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:40:13 +0300 Subject: [PATCH 03/38] Fix registry generator classpath for JDK8 tools API --- .../tools/generate-cn1-access-registry.sh | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/cn1playground/tools/generate-cn1-access-registry.sh b/scripts/cn1playground/tools/generate-cn1-access-registry.sh index f4a6a2d38d..2a1e393941 100755 --- a/scripts/cn1playground/tools/generate-cn1-access-registry.sh +++ b/scripts/cn1playground/tools/generate-cn1-access-registry.sh @@ -62,9 +62,25 @@ else fi mkdir -p "$BUILD_DIR" -javac -d "$BUILD_DIR" "$SRC" +TOOLS_JAR="${JAVA_HOME:-}/lib/tools.jar" +EXTRA_CP="" +if [ -f "$TOOLS_JAR" ]; then + EXTRA_CP="$TOOLS_JAR" +fi + +if [ -n "$EXTRA_CP" ]; then + javac -cp "$EXTRA_CP" -d "$BUILD_DIR" "$SRC" +else + javac -d "$BUILD_DIR" "$SRC" +fi + +RUNTIME_CP="$BUILD_DIR" +if [ -n "$EXTRA_CP" ]; then + RUNTIME_CP="$BUILD_DIR:$EXTRA_CP" +fi + if [ -n "$CN1_SOURCE_ROOTS_VALUE" ]; then - CN1_SOURCE_ROOTS="$CN1_SOURCE_ROOTS_VALUE" java -cp "$BUILD_DIR" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT" + CN1_SOURCE_ROOTS="$CN1_SOURCE_ROOTS_VALUE" java -cp "$RUNTIME_CP" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT" else - java -cp "$BUILD_DIR" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT" + java -cp "$RUNTIME_CP" com.codenameone.playground.tools.GenerateCN1AccessRegistry "$OUT" fi From b29fbaea90cd3b6fb9b425aa1bf41ea72d231084 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:54:20 +0300 Subject: [PATCH 04/38] Use Java 17 and portable checks in playground smoke CI --- .github/workflows/cn1playground-language.yml | 4 ++-- scripts/cn1playground/tools/run-playground-smoke-tests.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cn1playground-language.yml b/.github/workflows/cn1playground-language.yml index e940c8fbc7..73dc6828f3 100644 --- a/.github/workflows/cn1playground-language.yml +++ b/.github/workflows/cn1playground-language.yml @@ -25,11 +25,11 @@ jobs: - name: Check out repository uses: actions/checkout@v4 - - name: Set up Java 8 + - name: Set up Java 17 uses: actions/setup-java@v4 with: distribution: temurin - java-version: '8' + java-version: '17' cache: maven - name: Run playground language smoke tests diff --git a/scripts/cn1playground/tools/run-playground-smoke-tests.sh b/scripts/cn1playground/tools/run-playground-smoke-tests.sh index 2e25794b41..1bc912a5b1 100644 --- a/scripts/cn1playground/tools/run-playground-smoke-tests.sh +++ b/scripts/cn1playground/tools/run-playground-smoke-tests.sh @@ -8,14 +8,14 @@ echo "Regenerating CN1 access registry from release sources..." CN1_ACCESS_USE_LOCAL_SOURCES=false bash "$ROOT/tools/generate-cn1-access-registry.sh" echo "Verifying Component is present in generated registry..." -if ! rg -q 'index.put\("com\.codename1\.ui\.Component"' "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then +if ! grep -q 'index.put("com.codename1.ui.Component"' "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then echo "GeneratedCN1Access is missing com.codename1.ui.Component" >&2 exit 1 fi echo "Verifying key com.codename1.ui classes are present in generated registry..." for cls in Button Container Dialog Display Form Label List TextField BrowserComponent; do - if ! rg -q "index.put\\(\"com\\.codename1\\.ui\\.${cls}\"" "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then + if ! grep -q "index.put(\"com.codename1.ui.${cls}\"" "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then echo "GeneratedCN1Access is missing com.codename1.ui.${cls}" >&2 exit 1 fi @@ -23,7 +23,7 @@ done echo "Verifying package-private/internal sentinel classes are NOT generated..." for cls in com.codename1.ui.Accessor com.codename1.io.IOAccessor; do - if rg -q "index.put\\(\"${cls}\"" "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then + if grep -q "index.put(\"${cls}\"" "$ROOT/common/src/main/java/bsh/cn1/GeneratedCN1Access.java"; then echo "GeneratedCN1Access unexpectedly includes internal class ${cls}" >&2 exit 1 fi From 73abb3b08cf2037e89e521248014b4338f28c5ac Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:02:12 +0300 Subject: [PATCH 05/38] Build dependent modules in playground smoke Maven step --- scripts/cn1playground/tools/run-playground-smoke-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cn1playground/tools/run-playground-smoke-tests.sh b/scripts/cn1playground/tools/run-playground-smoke-tests.sh index 1bc912a5b1..e4d1bfaf51 100644 --- a/scripts/cn1playground/tools/run-playground-smoke-tests.sh +++ b/scripts/cn1playground/tools/run-playground-smoke-tests.sh @@ -29,6 +29,6 @@ for cls in com.codename1.ui.Accessor com.codename1.io.IOAccessor; do fi done -mvn -pl common -DskipTests test-compile org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ +mvn -pl common -am -DskipTests test-compile org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ -Dexec.classpathScope=test \ -Dexec.mainClass=com.codenameone.playground.PlaygroundSmokeHarness From e91778b3e51fae06dcf8c355196369f9d4ec6abb Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:08:27 +0300 Subject: [PATCH 06/38] Run playground smoke harness from common module classpath --- scripts/cn1playground/tools/run-playground-smoke-tests.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/cn1playground/tools/run-playground-smoke-tests.sh b/scripts/cn1playground/tools/run-playground-smoke-tests.sh index e4d1bfaf51..3b60ebbc0d 100644 --- a/scripts/cn1playground/tools/run-playground-smoke-tests.sh +++ b/scripts/cn1playground/tools/run-playground-smoke-tests.sh @@ -29,6 +29,7 @@ for cls in com.codename1.ui.Accessor com.codename1.io.IOAccessor; do fi done -mvn -pl common -am -DskipTests test-compile org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ +mvn -pl common -am -DskipTests test-compile +mvn -f common/pom.xml -DskipTests org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ -Dexec.classpathScope=test \ -Dexec.mainClass=com.codenameone.playground.PlaygroundSmokeHarness From 1f9482d14d22144b693b95a547d5b98ab9951d7e Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:28:11 +0300 Subject: [PATCH 07/38] Run playground smoke tests under Xvfb in CI --- .github/workflows/cn1playground-language.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cn1playground-language.yml b/.github/workflows/cn1playground-language.yml index 73dc6828f3..de946be799 100644 --- a/.github/workflows/cn1playground-language.yml +++ b/.github/workflows/cn1playground-language.yml @@ -32,8 +32,16 @@ jobs: java-version: '17' cache: maven + - name: Ensure Xvfb is available + run: | + set -euo pipefail + if ! command -v xvfb-run >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y xvfb + fi + - name: Run playground language smoke tests run: | set -euo pipefail cd scripts/cn1playground - bash tools/run-playground-smoke-tests.sh + xvfb-run -a bash tools/run-playground-smoke-tests.sh From e1285ac4dc8639279a03837aecd1947525b67f57 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:01:05 +0300 Subject: [PATCH 08/38] Install reactor artifacts before running playground harness --- scripts/cn1playground/tools/run-playground-smoke-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cn1playground/tools/run-playground-smoke-tests.sh b/scripts/cn1playground/tools/run-playground-smoke-tests.sh index 3b60ebbc0d..86cf61ba76 100644 --- a/scripts/cn1playground/tools/run-playground-smoke-tests.sh +++ b/scripts/cn1playground/tools/run-playground-smoke-tests.sh @@ -29,7 +29,7 @@ for cls in com.codename1.ui.Accessor com.codename1.io.IOAccessor; do fi done -mvn -pl common -am -DskipTests test-compile +mvn -pl common -am -DskipTests install mvn -f common/pom.xml -DskipTests org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ -Dexec.classpathScope=test \ -Dexec.mainClass=com.codenameone.playground.PlaygroundSmokeHarness From 792e546ff7ea7cb65cd3dfca21c2e6e3b9ecc0f5 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:40:23 +0300 Subject: [PATCH 09/38] Force smoke harness process exit after success --- .../com/codenameone/playground/PlaygroundSmokeHarness.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSmokeHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSmokeHarness.java index c7ac006b9f..c03bb872df 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSmokeHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSmokeHarness.java @@ -26,6 +26,9 @@ public static void main(String[] args) throws Exception { smokeComponentTypeResolvesWithoutExplicitImport(); smokeUIManagerClassImportDoesNotCollideWithGlobals(); System.out.println("Playground smoke tests passed."); + // Codename One/JavaSE initialization may leave non-daemon threads running. + // Force a clean exit so CI jobs don't hang after successful completion. + System.exit(0); } private static void smokeGeneratedRegistry() throws Exception { From a6903ea8a2bc19a58a30a356cf9e1230af5ae038 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:01:20 +0300 Subject: [PATCH 10/38] Add playground syntax matrix harness to smoke test pipeline --- .../PlaygroundSyntaxMatrixHarness.java | 133 ++++++++++++++++++ .../tools/run-playground-smoke-tests.sh | 3 + 2 files changed, 136 insertions(+) create mode 100644 scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java new file mode 100644 index 0000000000..2ffef33d5d --- /dev/null +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -0,0 +1,133 @@ +package com.codenameone.playground; + +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BorderLayout; +import java.util.ArrayList; +import java.util.List; + +/** + * Syntax regression matrix for Playground language support. + * + *

When adding new syntax support, update the expected outcome for the + * relevant test case from FAILURE to SUCCESS.

+ */ +public final class PlaygroundSyntaxMatrixHarness { + private PlaygroundSyntaxMatrixHarness() { + } + + private enum ExpectedOutcome { + SUCCESS, + FAILURE + } + + private static final class Case { + final String name; + final ExpectedOutcome expected; + final String script; + + Case(String name, ExpectedOutcome expected, String script) { + this.name = name; + this.expected = expected; + this.script = script; + } + } + + public static void main(String[] args) { + List cases = new ArrayList(); + // Control cases that should stay green. + cases.add(new Case("lambda_listener", ExpectedOutcome.SUCCESS, + "import com.codename1.ui.*;\n" + + "import com.codename1.ui.layouts.*;\n" + + "Container root = new Container(BoxLayout.y());\n" + + "Button b = new Button(\"Go\");\n" + + "b.addActionListener(e -> {});\n" + + "root.add(b);\n" + + "root;\n")); + cases.add(new Case("anonymous_listener", ExpectedOutcome.SUCCESS, + "import com.codename1.ui.*;\n" + + "import com.codename1.ui.events.*;\n" + + "import com.codename1.ui.layouts.*;\n" + + "Container root = new Container(BoxLayout.y());\n" + + "Button b = new Button(\"Go\");\n" + + "b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });\n" + + "root.add(b);\n" + + "root;\n")); + + // Known syntax gaps: flip these to SUCCESS as support lands. + cases.add(new Case("method_reference", ExpectedOutcome.FAILURE, + "import com.codename1.ui.*;\n" + + "import com.codename1.ui.layouts.*;\n" + + "Container root = new Container(BoxLayout.y());\n" + + "Button b = new Button(\"Go\");\n" + + "b.addActionListener(System.out::println);\n" + + "root.add(b);\n" + + "root;\n")); + cases.add(new Case("try_with_resources_multi", ExpectedOutcome.FAILURE, + "import com.codename1.ui.*;\n" + + "import com.codename1.ui.layouts.*;\n" + + "import java.io.*;\n" + + "Container root = new Container(BoxLayout.y());\n" + + "try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1}); ByteArrayOutputStream out = new ByteArrayOutputStream()) {\n" + + " out.write(in.read());\n" + + "}\n" + + "root;\n")); + cases.add(new Case("enhanced_for_array", ExpectedOutcome.FAILURE, + "import com.codename1.ui.*;\n" + + "import com.codename1.ui.layouts.*;\n" + + "Container root = new Container(BoxLayout.y());\n" + + "int sum = 0;\n" + + "for (int v : new int[]{1,2,3}) {\n" + + " sum += v;\n" + + "}\n" + + "root.add(new Label(\"sum=\" + sum));\n" + + "root;\n")); + + int passed = 0; + for (Case testCase : cases) { + PlaygroundRunner.RunResult result = runSnippet(testCase.script); + boolean success = result.getComponent() instanceof Component; + boolean casePassed = testCase.expected == ExpectedOutcome.SUCCESS ? success : !success; + if (!casePassed) { + throw new IllegalStateException("Case failed: " + testCase.name + + " expected=" + testCase.expected + " messages=" + summarizeMessages(result)); + } + passed++; + } + + System.out.println("Playground syntax matrix passed (" + passed + "/" + cases.size() + ")."); + System.exit(0); + } + + private static PlaygroundRunner.RunResult runSnippet(String script) { + Display.init(null); + Form host = new Form("Host", new BorderLayout()); + Container preview = new Container(new BorderLayout()); + host.add(BorderLayout.CENTER, preview); + host.show(); + + PlaygroundContext context = new PlaygroundContext(host, preview, null, new PlaygroundContext.Logger() { + public void log(String message) { + } + }); + PlaygroundRunner runner = new PlaygroundRunner(); + return runner.run(script, context); + } + + private static String summarizeMessages(PlaygroundRunner.RunResult result) { + List messages = result.getMessages(); + if (messages.isEmpty()) { + return ""; + } + StringBuilder out = new StringBuilder(); + for (int i = 0; i < messages.size(); i++) { + if (i > 0) { + out.append(" | "); + } + out.append(messages.get(i).text); + } + return out.toString(); + } +} diff --git a/scripts/cn1playground/tools/run-playground-smoke-tests.sh b/scripts/cn1playground/tools/run-playground-smoke-tests.sh index 86cf61ba76..32a67b5b84 100644 --- a/scripts/cn1playground/tools/run-playground-smoke-tests.sh +++ b/scripts/cn1playground/tools/run-playground-smoke-tests.sh @@ -33,3 +33,6 @@ mvn -pl common -am -DskipTests install mvn -f common/pom.xml -DskipTests org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ -Dexec.classpathScope=test \ -Dexec.mainClass=com.codenameone.playground.PlaygroundSmokeHarness +mvn -f common/pom.xml -DskipTests org.codehaus.mojo:exec-maven-plugin:3.0.0:java \ + -Dexec.classpathScope=test \ + -Dexec.mainClass=com.codenameone.playground.PlaygroundSyntaxMatrixHarness From e08c2eb36f347155b54276fb8e01bc79203d2c7b Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:01:25 +0300 Subject: [PATCH 11/38] Use Java text blocks in syntax matrix snippets --- .../PlaygroundSyntaxMatrixHarness.java | 88 +++++++++++-------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index 2ffef33d5d..b0e771ee2a 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -39,51 +39,61 @@ public static void main(String[] args) { List cases = new ArrayList(); // Control cases that should stay green. cases.add(new Case("lambda_listener", ExpectedOutcome.SUCCESS, - "import com.codename1.ui.*;\n" - + "import com.codename1.ui.layouts.*;\n" - + "Container root = new Container(BoxLayout.y());\n" - + "Button b = new Button(\"Go\");\n" - + "b.addActionListener(e -> {});\n" - + "root.add(b);\n" - + "root;\n")); + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(e -> {}); + root.add(b); + root; + """)); cases.add(new Case("anonymous_listener", ExpectedOutcome.SUCCESS, - "import com.codename1.ui.*;\n" - + "import com.codename1.ui.events.*;\n" - + "import com.codename1.ui.layouts.*;\n" - + "Container root = new Container(BoxLayout.y());\n" - + "Button b = new Button(\"Go\");\n" - + "b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });\n" - + "root.add(b);\n" - + "root;\n")); + """ + import com.codename1.ui.*; + import com.codename1.ui.events.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} }); + root.add(b); + root; + """)); // Known syntax gaps: flip these to SUCCESS as support lands. cases.add(new Case("method_reference", ExpectedOutcome.FAILURE, - "import com.codename1.ui.*;\n" - + "import com.codename1.ui.layouts.*;\n" - + "Container root = new Container(BoxLayout.y());\n" - + "Button b = new Button(\"Go\");\n" - + "b.addActionListener(System.out::println);\n" - + "root.add(b);\n" - + "root;\n")); + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(System.out::println); + root.add(b); + root; + """)); cases.add(new Case("try_with_resources_multi", ExpectedOutcome.FAILURE, - "import com.codename1.ui.*;\n" - + "import com.codename1.ui.layouts.*;\n" - + "import java.io.*;\n" - + "Container root = new Container(BoxLayout.y());\n" - + "try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1}); ByteArrayOutputStream out = new ByteArrayOutputStream()) {\n" - + " out.write(in.read());\n" - + "}\n" - + "root;\n")); + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + import java.io.*; + Container root = new Container(BoxLayout.y()); + try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1}); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + out.write(in.read()); + } + root; + """)); cases.add(new Case("enhanced_for_array", ExpectedOutcome.FAILURE, - "import com.codename1.ui.*;\n" - + "import com.codename1.ui.layouts.*;\n" - + "Container root = new Container(BoxLayout.y());\n" - + "int sum = 0;\n" - + "for (int v : new int[]{1,2,3}) {\n" - + " sum += v;\n" - + "}\n" - + "root.add(new Label(\"sum=\" + sum));\n" - + "root;\n")); + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + int sum = 0; + for (int v : new int[]{1,2,3}) { + sum += v; + } + root.add(new Label("sum=" + sum)); + root; + """)); int passed = 0; for (Case testCase : cases) { From d2b1cc727487358e4405b73a0331f860ba841b83 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:55:37 +0300 Subject: [PATCH 12/38] Stabilize syntax matrix outcomes and always exit process --- .../PlaygroundSyntaxMatrixHarness.java | 145 +++++++++--------- 1 file changed, 76 insertions(+), 69 deletions(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index b0e771ee2a..1beda3da2d 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -36,79 +36,86 @@ private static final class Case { } public static void main(String[] args) { - List cases = new ArrayList(); - // Control cases that should stay green. - cases.add(new Case("lambda_listener", ExpectedOutcome.SUCCESS, - """ - import com.codename1.ui.*; - import com.codename1.ui.layouts.*; - Container root = new Container(BoxLayout.y()); - Button b = new Button("Go"); - b.addActionListener(e -> {}); - root.add(b); - root; - """)); - cases.add(new Case("anonymous_listener", ExpectedOutcome.SUCCESS, - """ - import com.codename1.ui.*; - import com.codename1.ui.events.*; - import com.codename1.ui.layouts.*; - Container root = new Container(BoxLayout.y()); - Button b = new Button("Go"); - b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} }); - root.add(b); - root; - """)); + int exitCode = 0; + try { + List cases = new ArrayList(); + // Control cases that should stay green. + cases.add(new Case("lambda_listener", ExpectedOutcome.SUCCESS, + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(e -> {}); + root.add(b); + root; + """)); + cases.add(new Case("anonymous_listener", ExpectedOutcome.SUCCESS, + """ + import com.codename1.ui.*; + import com.codename1.ui.events.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} }); + root.add(b); + root; + """)); + cases.add(new Case("enhanced_for_array", ExpectedOutcome.SUCCESS, + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + int sum = 0; + for (int v : new int[]{1,2,3}) { + sum += v; + } + root.add(new Label("sum=" + sum)); + root; + """)); - // Known syntax gaps: flip these to SUCCESS as support lands. - cases.add(new Case("method_reference", ExpectedOutcome.FAILURE, - """ - import com.codename1.ui.*; - import com.codename1.ui.layouts.*; - Container root = new Container(BoxLayout.y()); - Button b = new Button("Go"); - b.addActionListener(System.out::println); - root.add(b); - root; - """)); - cases.add(new Case("try_with_resources_multi", ExpectedOutcome.FAILURE, - """ - import com.codename1.ui.*; - import com.codename1.ui.layouts.*; - import java.io.*; - Container root = new Container(BoxLayout.y()); - try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1}); ByteArrayOutputStream out = new ByteArrayOutputStream()) { - out.write(in.read()); - } - root; - """)); - cases.add(new Case("enhanced_for_array", ExpectedOutcome.FAILURE, - """ - import com.codename1.ui.*; - import com.codename1.ui.layouts.*; - Container root = new Container(BoxLayout.y()); - int sum = 0; - for (int v : new int[]{1,2,3}) { - sum += v; - } - root.add(new Label("sum=" + sum)); - root; - """)); + // Known syntax gaps: flip these to SUCCESS as support lands. + cases.add(new Case("method_reference", ExpectedOutcome.FAILURE, + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(System.out::println); + root.add(b); + root; + """)); + cases.add(new Case("try_with_resources_multi", ExpectedOutcome.FAILURE, + """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + import java.io.*; + Container root = new Container(BoxLayout.y()); + try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1}); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + out.write(in.read()); + } + root; + """)); - int passed = 0; - for (Case testCase : cases) { - PlaygroundRunner.RunResult result = runSnippet(testCase.script); - boolean success = result.getComponent() instanceof Component; - boolean casePassed = testCase.expected == ExpectedOutcome.SUCCESS ? success : !success; - if (!casePassed) { - throw new IllegalStateException("Case failed: " + testCase.name - + " expected=" + testCase.expected + " messages=" + summarizeMessages(result)); + int passed = 0; + for (Case testCase : cases) { + PlaygroundRunner.RunResult result = runSnippet(testCase.script); + boolean success = result.getComponent() instanceof Component; + boolean casePassed = testCase.expected == ExpectedOutcome.SUCCESS ? success : !success; + if (!casePassed) { + throw new IllegalStateException("Case failed: " + testCase.name + + " expected=" + testCase.expected + " messages=" + summarizeMessages(result)); + } + passed++; } - passed++; - } - System.out.println("Playground syntax matrix passed (" + passed + "/" + cases.size() + ")."); - System.exit(0); + System.out.println("Playground syntax matrix passed (" + passed + "/" + cases.size() + ")."); + } catch (Throwable t) { + t.printStackTrace(System.err); + exitCode = 1; + } finally { + System.exit(exitCode); + } } private static PlaygroundRunner.RunResult runSnippet(String script) { From 6c21e10827d55a36d4fc4b0dfe28fb053e06926b Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 22:27:12 +0300 Subject: [PATCH 13/38] Document playground smoke workflow and syntax rollout process --- scripts/cn1playground/README.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/scripts/cn1playground/README.md b/scripts/cn1playground/README.md index 46f1d0e35c..414c493a5e 100644 --- a/scripts/cn1playground/README.md +++ b/scripts/cn1playground/README.md @@ -347,9 +347,37 @@ mvn clean install ```bash cd scripts/cn1playground -./scripts/run-tests.sh +bash tools/run-playground-smoke-tests.sh ``` +This smoke command currently runs: + +1. CN1 access registry generation (`tools/generate-cn1-access-registry.sh`). +2. Registry sanity checks (expected/forbidden class entries). +3. `PlaygroundSmokeHarness` end-to-end behavior checks. +4. `PlaygroundSyntaxMatrixHarness` syntax regression checks. + +## Language Feature Rollout Process + +Use this process when adding or fixing Java syntax support in Playground: + +1. **Add/adjust matrix coverage first** + Update `common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java` with a focused snippet for the target syntax. + - For currently unsupported syntax, add as `ExpectedOutcome.FAILURE`. + - When support lands, flip that case to `ExpectedOutcome.SUCCESS`. + +2. **Implement parser/runtime change in small steps** + Prefer one syntax feature per PR (e.g. method references only) to keep regressions easy to isolate. + +3. **Run smoke + syntax matrix locally** + Run `bash tools/run-playground-smoke-tests.sh` from `scripts/cn1playground`. + +4. **Require CI green before merge** + The `CN1 Playground Language Tests` workflow runs the same smoke command under CI (`xvfb-run`) and should pass before merging syntax updates. + +5. **Document behavior changes** + Update this README's known issues/limitations when syntax support changes so users know what is now supported. + ## Known Issues 1. **Parse errors with complex expressions**: BeanShell's parser may fail on some Java syntax. Simplify complex expressions or break them into multiple statements. @@ -360,4 +388,4 @@ cd scripts/cn1playground ## Contributing -See the main Codename One repository for contribution guidelines. \ No newline at end of file +See the main Codename One repository for contribution guidelines. From c7cd472ec3ed243cf667dd01f984482ef9fa7106 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Tue, 7 Apr 2026 22:34:28 +0300 Subject: [PATCH 14/38] Expand syntax matrix to table-driven parse/eval coverage --- .../PlaygroundSyntaxMatrixHarness.java | 228 +++++++++++++++--- 1 file changed, 193 insertions(+), 35 deletions(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index 1beda3da2d..108e8aa995 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -1,6 +1,5 @@ package com.codenameone.playground; -import com.codename1.ui.Component; import com.codename1.ui.Container; import com.codename1.ui.Display; import com.codename1.ui.Form; @@ -9,10 +8,7 @@ import java.util.List; /** - * Syntax regression matrix for Playground language support. - * - *

When adding new syntax support, update the expected outcome for the - * relevant test case from FAILURE to SUCCESS.

+ * Table-driven syntax regression matrix for playground language support. */ public final class PlaygroundSyntaxMatrixHarness { private PlaygroundSyntaxMatrixHarness() { @@ -20,18 +16,21 @@ private PlaygroundSyntaxMatrixHarness() { private enum ExpectedOutcome { SUCCESS, - FAILURE + PARSE_ERROR, + EVAL_ERROR } private static final class Case { final String name; - final ExpectedOutcome expected; - final String script; + final String sourceSnippet; + final ExpectedOutcome expectedOutcome; + final String expectedDiagnosticSubstring; - Case(String name, ExpectedOutcome expected, String script) { + Case(String name, String sourceSnippet, ExpectedOutcome expectedOutcome, String expectedDiagnosticSubstring) { this.name = name; - this.expected = expected; - this.script = script; + this.sourceSnippet = sourceSnippet; + this.expectedOutcome = expectedOutcome; + this.expectedDiagnosticSubstring = expectedDiagnosticSubstring; } } @@ -39,9 +38,9 @@ public static void main(String[] args) { int exitCode = 0; try { List cases = new ArrayList(); - // Control cases that should stay green. - cases.add(new Case("lambda_listener", ExpectedOutcome.SUCCESS, - """ + + // Control cases (known good behavior). + cases.add(new Case("control_lambda_listener", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; Container root = new Container(BoxLayout.y()); @@ -49,9 +48,8 @@ public static void main(String[] args) { b.addActionListener(e -> {}); root.add(b); root; - """)); - cases.add(new Case("anonymous_listener", ExpectedOutcome.SUCCESS, - """ + """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("control_anonymous_listener", """ import com.codename1.ui.*; import com.codename1.ui.events.*; import com.codename1.ui.layouts.*; @@ -60,23 +58,21 @@ public static void main(String[] args) { b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} }); root.add(b); root; - """)); - cases.add(new Case("enhanced_for_array", ExpectedOutcome.SUCCESS, - """ + """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("control_classic_for_loop", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; Container root = new Container(BoxLayout.y()); int sum = 0; - for (int v : new int[]{1,2,3}) { - sum += v; + for (int i = 0; i < 3; i++) { + sum += i; } root.add(new Label("sum=" + sum)); root; - """)); + """, ExpectedOutcome.SUCCESS, null)); - // Known syntax gaps: flip these to SUCCESS as support lands. - cases.add(new Case("method_reference", ExpectedOutcome.FAILURE, - """ + // Method references. + cases.add(new Case("method_reference_type", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; Container root = new Container(BoxLayout.y()); @@ -84,9 +80,39 @@ public static void main(String[] args) { b.addActionListener(System.out::println); root.add(b); root; - """)); - cases.add(new Case("try_with_resources_multi", ExpectedOutcome.FAILURE, - """ + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + cases.add(new Case("method_reference_instance", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + String prefix = "X:"; + Button b = new Button("Go"); + b.addActionListener(prefix::concat); + root.add(b); + root; + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + cases.add(new Case("method_reference_constructor", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + import java.util.function.*; + Container root = new Container(BoxLayout.y()); + Supplier ctor = StringBuilder::new; + root.add(new Label(ctor.get().toString())); + root; + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + + // Try-with-resources variants. + cases.add(new Case("twr_single_resource", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + import java.io.*; + Container root = new Container(BoxLayout.y()); + try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1,2,3})) { + root.add(new Label("ok=" + in.read())); + } + root; + """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("twr_multiple_resources", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; import java.io.*; @@ -95,16 +121,125 @@ public static void main(String[] args) { out.write(in.read()); } root; - """)); + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + cases.add(new Case("twr_trailing_semicolon", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + import java.io.*; + Container root = new Container(BoxLayout.y()); + try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1});) { + root.add(new Label("ok")); + } + root; + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + cases.add(new Case("twr_nested_try_catch_finally", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + import java.io.*; + Container root = new Container(BoxLayout.y()); + try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1})) { + try { + root.add(new Label("inner")); + } catch (RuntimeException ex) { + root.add(new Label("catch")); + } finally { + root.add(new Label("finally")); + } + } + root; + """, ExpectedOutcome.SUCCESS, null)); + + // Enhanced-for arrays. + cases.add(new Case("enhanced_for_primitive_array", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + int sum = 0; + for (int v : new int[]{1,2,3}) { + sum += v; + } + root.add(new Label("sum=" + sum)); + root; + """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("enhanced_for_object_array", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + String txt = ""; + for (String v : new String[]{"a","b"}) { + txt += v; + } + root.add(new Label(txt)); + root; + """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("enhanced_for_null_array", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + int[] values = null; + for (int v : values) { + root.add(new Label("v=" + v)); + } + root; + """, ExpectedOutcome.EVAL_ERROR, "Evaluation error:")); + cases.add(new Case("enhanced_for_nested", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + int count = 0; + for (int row : new int[]{1,2}) { + for (int col : new int[]{3,4}) { + count += row + col; + } + } + root.add(new Label("count=" + count)); + root; + """, ExpectedOutcome.SUCCESS, null)); + + // Multiple classes / inner class variants. + cases.add(new Case("multiple_top_level_classes", """ + class A {} + class B {} + new A(); + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + cases.add(new Case("inner_class_static_member", """ + class Outer { + static class Inner { + String label() { return "ok"; } + } + } + new Outer.Inner().label(); + """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("inner_class_anonymous", """ + import com.codename1.ui.*; + import com.codename1.ui.events.*; + import com.codename1.ui.layouts.*; + Container root = new Container(BoxLayout.y()); + Button b = new Button("Go"); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) {} + }); + root.add(b); + root; + """, ExpectedOutcome.SUCCESS, null)); int passed = 0; for (Case testCase : cases) { - PlaygroundRunner.RunResult result = runSnippet(testCase.script); - boolean success = result.getComponent() instanceof Component; - boolean casePassed = testCase.expected == ExpectedOutcome.SUCCESS ? success : !success; - if (!casePassed) { + PlaygroundRunner.RunResult result = runSnippet(testCase.sourceSnippet); + ExpectedOutcome actual = classify(result); + if (actual != testCase.expectedOutcome) { throw new IllegalStateException("Case failed: " + testCase.name - + " expected=" + testCase.expected + " messages=" + summarizeMessages(result)); + + " expected=" + testCase.expectedOutcome + + " actual=" + actual + + " messages=" + summarizeMessages(result)); + } + if (testCase.expectedDiagnosticSubstring != null) { + String message = firstDiagnosticMessage(result); + if (message == null || message.indexOf(testCase.expectedDiagnosticSubstring) < 0) { + throw new IllegalStateException("Case failed: " + testCase.name + + " expected diagnostic containing='" + testCase.expectedDiagnosticSubstring + + "' actual='" + message + "'"); + } } passed++; } @@ -118,6 +253,29 @@ public static void main(String[] args) { } } + private static ExpectedOutcome classify(PlaygroundRunner.RunResult result) { + if (result.getComponent() != null) { + return ExpectedOutcome.SUCCESS; + } + String message = firstDiagnosticMessage(result); + if (message != null && message.startsWith("Parse error:")) { + return ExpectedOutcome.PARSE_ERROR; + } + return ExpectedOutcome.EVAL_ERROR; + } + + private static String firstDiagnosticMessage(PlaygroundRunner.RunResult result) { + List diagnostics = result.getDiagnostics(); + if (!diagnostics.isEmpty()) { + return diagnostics.get(0).message; + } + List messages = result.getMessages(); + if (!messages.isEmpty()) { + return messages.get(0).text; + } + return null; + } + private static PlaygroundRunner.RunResult runSnippet(String script) { Display.init(null); Form host = new Form("Host", new BorderLayout()); From aecea500ad6e06a857dc36de6c3088c22ba26b17 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:28:31 +0300 Subject: [PATCH 15/38] Mark try-with-resources matrix cases as current parse gaps --- .../codenameone/playground/PlaygroundSyntaxMatrixHarness.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index 108e8aa995..3bb884a40f 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -111,7 +111,7 @@ public static void main(String[] args) { root.add(new Label("ok=" + in.read())); } root; - """, ExpectedOutcome.SUCCESS, null)); + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); cases.add(new Case("twr_multiple_resources", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; @@ -147,7 +147,7 @@ public static void main(String[] args) { } } root; - """, ExpectedOutcome.SUCCESS, null)); + """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); // Enhanced-for arrays. cases.add(new Case("enhanced_for_primitive_array", """ From a9281ef1d4c9114f1cf85d500e2ecdba941f063d Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:49:30 +0300 Subject: [PATCH 16/38] Enable try-with-resources without catch/finally in parser --- scripts/cn1playground/common/src/main/java/bsh/Parser.java | 1 + .../playground/PlaygroundSyntaxMatrixHarness.java | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/Parser.java b/scripts/cn1playground/common/src/main/java/bsh/Parser.java index d50a6cf079..2c369bbff8 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/Parser.java +++ b/scripts/cn1playground/common/src/main/java/bsh/Parser.java @@ -4546,6 +4546,7 @@ final public void TryStatement() throws ParseException {/*@bgen(jjtree) TryState switch (jj_ntk == -1 ? jj_ntk_f() : jj_ntk) { case LPAREN:{ TryWithResources(); +closed = true; break; } default: diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index 3bb884a40f..c40c71c826 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -111,7 +111,7 @@ public static void main(String[] args) { root.add(new Label("ok=" + in.read())); } root; - """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("twr_multiple_resources", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; @@ -121,7 +121,7 @@ public static void main(String[] args) { out.write(in.read()); } root; - """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("twr_trailing_semicolon", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; @@ -147,7 +147,7 @@ public static void main(String[] args) { } } root; - """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + """, ExpectedOutcome.SUCCESS, null)); // Enhanced-for arrays. cases.add(new Case("enhanced_for_primitive_array", """ From 8af87c8e4d94ce41d738a02b4b8cb4bd841f9764 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:49:55 +0300 Subject: [PATCH 17/38] Throw evaluation error for enhanced-for over null --- .../common/src/main/java/bsh/BSHEnhancedForStatement.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/cn1playground/common/src/main/java/bsh/BSHEnhancedForStatement.java b/scripts/cn1playground/common/src/main/java/bsh/BSHEnhancedForStatement.java index 02788ca21b..b194b0cc32 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/BSHEnhancedForStatement.java +++ b/scripts/cn1playground/common/src/main/java/bsh/BSHEnhancedForStatement.java @@ -67,6 +67,9 @@ public Object eval(CallStack callstack, Interpreter interpreter) throws EvalErro statement = nodeCount > 1 ? jjtGetChild(1) : null; } final Object iteratee = expression.eval(callstack, interpreter); + if (iteratee == null || iteratee == Primitive.NULL) { + throw new EvalException("Cannot iterate over null value", this, callstack); + } final CollectionManager cm = CollectionManager.getCollectionManager(); final Iterator iterator = cm.getBshIterator(iteratee); try { From a72f2af63a450d67f8d71db4caa412f0f35f43c8 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:05:18 +0300 Subject: [PATCH 18/38] Use local AutoCloseable resources in TWR matrix cases --- .../PlaygroundSyntaxMatrixHarness.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index c40c71c826..dcc0f176c0 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -105,29 +105,35 @@ public static void main(String[] args) { cases.add(new Case("twr_single_resource", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - import java.io.*; + class Res implements AutoCloseable { + public void close() {} + } Container root = new Container(BoxLayout.y()); - try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1,2,3})) { - root.add(new Label("ok=" + in.read())); + try (Res in = new Res()) { + root.add(new Label("ok")); } root; """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("twr_multiple_resources", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - import java.io.*; + class Res implements AutoCloseable { + public void close() {} + } Container root = new Container(BoxLayout.y()); - try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1}); ByteArrayOutputStream out = new ByteArrayOutputStream()) { - out.write(in.read()); + try (Res in = new Res(); Res out = new Res()) { + root.add(new Label("ok")); } root; """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("twr_trailing_semicolon", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - import java.io.*; + class Res implements AutoCloseable { + public void close() {} + } Container root = new Container(BoxLayout.y()); - try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1});) { + try (Res in = new Res();) { root.add(new Label("ok")); } root; @@ -135,9 +141,11 @@ public static void main(String[] args) { cases.add(new Case("twr_nested_try_catch_finally", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - import java.io.*; + class Res implements AutoCloseable { + public void close() {} + } Container root = new Container(BoxLayout.y()); - try (ByteArrayInputStream in = new ByteArrayInputStream(new byte[]{1})) { + try (Res in = new Res()) { try { root.add(new Label("inner")); } catch (RuntimeException ex) { From c60e28b0dd9907c818b698a87fb6c8031f8239ed Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:51:29 +0300 Subject: [PATCH 19/38] Use StringReader resources in TWR matrix cases --- .../PlaygroundSyntaxMatrixHarness.java | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index dcc0f176c0..3618a6253c 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -105,11 +105,9 @@ public static void main(String[] args) { cases.add(new Case("twr_single_resource", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - class Res implements AutoCloseable { - public void close() {} - } + import java.io.*; Container root = new Container(BoxLayout.y()); - try (Res in = new Res()) { + try (StringReader in = new StringReader("abc")) { root.add(new Label("ok")); } root; @@ -117,11 +115,9 @@ public void close() {} cases.add(new Case("twr_multiple_resources", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - class Res implements AutoCloseable { - public void close() {} - } + import java.io.*; Container root = new Container(BoxLayout.y()); - try (Res in = new Res(); Res out = new Res()) { + try (StringReader in = new StringReader("a"); StringReader out = new StringReader("b")) { root.add(new Label("ok")); } root; @@ -129,11 +125,9 @@ public void close() {} cases.add(new Case("twr_trailing_semicolon", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - class Res implements AutoCloseable { - public void close() {} - } + import java.io.*; Container root = new Container(BoxLayout.y()); - try (Res in = new Res();) { + try (StringReader in = new StringReader("a");) { root.add(new Label("ok")); } root; @@ -141,11 +135,9 @@ public void close() {} cases.add(new Case("twr_nested_try_catch_finally", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - class Res implements AutoCloseable { - public void close() {} - } + import java.io.*; Container root = new Container(BoxLayout.y()); - try (Res in = new Res()) { + try (StringReader in = new StringReader("abc")) { try { root.add(new Label("inner")); } catch (RuntimeException ex) { From a9f1b8855d926cb2fbe54a4d0b5e609eb79ca862 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:54:58 +0300 Subject: [PATCH 20/38] Enable BeanShell class generation for inline class declarations --- .../src/main/java/bsh/ClassGenerator.java | 302 ++++- .../src/main/java/bsh/ClassGeneratorUtil.java | 1000 ++++++++++++++++- .../PlaygroundSyntaxMatrixHarness.java | 6 +- 3 files changed, 1272 insertions(+), 36 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index d1f4a1488f..4f501078af 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -15,54 +15,296 @@ * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * + * * + * * + * This file is part of the BeanShell Java Scripting distribution. * + * Documentation and updates may be found at http://www.beanshell.org/ * + * Patrick Niemeyer (pat@pat.net) * + * Author of Learning Java, O'Reilly & Associates * + * * *****************************************************************************/ - package bsh; -/** - * Scripted class generation is not supported in the CN1 playground runtime. - * This stub preserves parser/runtime references while failing explicitly if a - * script attempts to declare or synthesize classes. - */ +import static bsh.This.Keys.BSHSUPER; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + public final class ClassGenerator { - public enum Type { - CLASS, INTERFACE, ENUM + + enum Type { CLASS, INTERFACE, ENUM } + + private static ClassGenerator cg; + + public static ClassGenerator getClassGenerator() { + if (cg == null) { + cg = new ClassGenerator(); + } + + return cg; + } + + /** + * Parse the BSHBlock for the class definition and generate the class. + */ + public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { + // Delegate to the static method + return generateClassImpl(name, modifiers, interfaces, superClass, block, type, callstack, interpreter); } - public static final class ClassNodeFilter implements BSHBlock.NodeFilter { - public static final ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(); + /** + * Invoke a super.method() style superclass method on an object instance. + * This is not a normal function of the Java reflection API and is + * provided by generated class accessor methods. + */ + public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError, InvocationTargetException { + // Delegate to the static method + return invokeSuperclassMethodImpl(bcm, instance, classStatic, methodName, args); + } + + /** + * Parse the BSHBlock for for the class definition and generate the class + * using ClassGenerator. + */ + public static Class generateClassImpl(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { + NameSpace enclosingNameSpace = callstack.top(); + String packageName = enclosingNameSpace.getPackage(); + String className = enclosingNameSpace.isClass ? (enclosingNameSpace.getName() + "$" + name) : name; + String fqClassName = packageName == null ? className : packageName + "." + className; + BshClassManager bcm = interpreter.getClassManager(); + + // Create the class static namespace + NameSpace classStaticNameSpace = new NameSpace(enclosingNameSpace, className); + classStaticNameSpace.isClass = true; + + callstack.push(classStaticNameSpace); - private ClassNodeFilter() { + // Evaluate any inner class class definitions in the block + // effectively recursively call this method for contained classes first + block.evalBlock(callstack, interpreter, true/*override*/, ClassNodeFilter.CLASSCLASSES); + + // Generate the type for our class + Variable[] variables = getDeclaredVariables(block, callstack, interpreter, packageName); + DelayedEvalBshMethod[] methods = getDeclaredMethods(block, callstack, interpreter, packageName, superClass); + + callstack.pop(); + + // initialize static this singleton in namespace + classStaticNameSpace.getThis(interpreter); + + // Create the class generator, which encapsulates all knowledge of the + // structure of the class + ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(modifiers, className, packageName, superClass, interfaces, variables, methods, classStaticNameSpace, type); + + // Let the class generator install hooks relating to the structure of + // the class into the class static namespace. e.g. the constructor + // array. This is necessary whether we are generating code or just + // reinitializing a previously generated class. + classGenerator.initStaticNameSpace(classStaticNameSpace, block/*instance initializer*/); + + // Check for existing class (saved class file) + Class genClass = bcm.getAssociatedClass(fqClassName); + + // If the class isn't there then generate it. + // Else just let it be initialized below. + if (genClass == null) { + // generate bytecode, optionally with static init hooks to + // bootstrap the interpreter + byte[] code = classGenerator.generateClass(); + + if (Interpreter.getSaveClasses()) + saveClasses(className, code); + + // Define the new class in the classloader + genClass = bcm.defineClass(fqClassName, code); + Interpreter.debug("Define ", fqClassName, " as ", genClass); } + // import the unqualified class name into parent namespace + enclosingNameSpace.importClass(fqClassName.replace('$', '.')); - public boolean isVisible(Node node) { - return false; + // Give the static space its class static import + // important to do this after all classes are defined + classStaticNameSpace.setClassStatic(genClass); + + Interpreter.debug(classStaticNameSpace); + + if (interpreter.getStrictJava()) + ClassGeneratorUtil.checkAbstractMethodImplementation(genClass); + + return genClass; + } + + private static void saveClasses(String className, byte[] code) { + String dir = Interpreter.getSaveClassesDir(); + if (dir != null) try + ( FileOutputStream out = new FileOutputStream(dir + "/" + className + ".class") ) { + out.write(code); + } catch (IOException e) { + e.printStackTrace(); } } - private static final ClassGenerator INSTANCE = new ClassGenerator(); + static Variable[] getDeclaredVariables(BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) { + List vars = new ArrayList(); + for (int child = 0; child < body.jjtGetNumChildren(); child++) { + Node node = body.jjtGetChild(child); + if (node instanceof BSHEnumConstant) { + BSHEnumConstant enm = (BSHEnumConstant) node; + try { + Variable var = new Variable(enm.getName(), + enm.getType(), null/*value*/, enm.mods); + vars.add(var); + } catch (UtilEvalError e) { + // value error shouldn't happen + } + } else if (node instanceof BSHTypedVariableDeclaration) { + BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node; + Modifiers modifiers = tvd.modifiers; + BSHVariableDeclarator[] vardec = tvd.getDeclarators(); + for (BSHVariableDeclarator aVardec : vardec) { + String name = aVardec.name; + try { + Class type = tvd.evalType(callstack, interpreter); + Variable var = new Variable(name, type, null/*value*/, modifiers); + vars.add(var); + } catch (UtilEvalError | EvalError e) { + // value error shouldn't happen + } + } + } + } - private ClassGenerator() { + return vars.toArray(new Variable[vars.size()]); } - public static ClassGenerator getClassGenerator() { - return INSTANCE; + static DelayedEvalBshMethod[] getDeclaredMethods(BSHBlock body, + CallStack callstack, Interpreter interpreter, String defaultPackage, + Class superClass) throws EvalError { + List methods = new ArrayList<>(); + if ( callstack.top().getName().indexOf("$anon") > -1 ) { + // anonymous classes need super constructor + String classBaseName = Types.getBaseName(callstack.top().getName()); + Invocable con = BshClassManager.memberCache.get(superClass) + .findMethod(superClass.getName(), + This.CONTEXT_ARGS.get().get(classBaseName)); + DelayedEvalBshMethod bm = new DelayedEvalBshMethod(classBaseName, con, callstack.top()); + methods.add(bm); + } + for (int child = 0; child < body.jjtGetNumChildren(); child++) { + Node node = body.jjtGetChild(child); + if (node instanceof BSHMethodDeclaration) { + BSHMethodDeclaration md = (BSHMethodDeclaration) node; + md.insureNodesParsed(); + Modifiers modifiers = md.modifiers; + String name = md.name; + String returnType = md.getReturnTypeDescriptor(callstack, interpreter, defaultPackage); + BSHReturnType returnTypeNode = md.getReturnTypeNode(); + BSHFormalParameters paramTypesNode = md.paramsNode; + String[] paramTypes = paramTypesNode.getTypeDescriptors(callstack, interpreter, defaultPackage); + + DelayedEvalBshMethod bm = new DelayedEvalBshMethod(name, returnType, returnTypeNode, md.paramsNode.getParamNames(), paramTypes, paramTypesNode, md.blockNode, null/*declaringNameSpace*/, modifiers, md.isVarArgs, callstack, interpreter); + + methods.add(bm); + } + } + return methods.toArray(new DelayedEvalBshMethod[methods.size()]); } - public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, - Class superClass, BSHBlock block, Type type, CallStack callstack, - Interpreter interpreter) throws EvalError { - throw new EvalError("Scripted class generation is not supported in the Codename One BeanShell runtime.", - null, callstack); + + /** + * A node filter that filters nodes for either a class body static + * initializer or instance initializer. In the static case only static + * members are passed, etc. + */ + static class ClassNodeFilter implements BSHBlock.NodeFilter { + private enum Context { STATIC, INSTANCE, CLASSES } + private enum Types { ALL, METHODS, FIELDS } + public static ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(Context.STATIC, Types.FIELDS); + public static ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(Context.STATIC, Types.METHODS); + public static ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(Context.INSTANCE, Types.FIELDS); + public static ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(Context.INSTANCE, Types.METHODS); + public static ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(Context.CLASSES); + + Context context; + Types types = Types.ALL; + + private ClassNodeFilter(Context context) { + this.context = context; + } + + private ClassNodeFilter(Context context, Types types) { + this.context = context; + this.types = types; + } + + @Override + public boolean isVisible(Node node) { + if (context == Context.CLASSES) return node instanceof BSHClassDeclaration; + + // Only show class decs in CLASSES + if (node instanceof BSHClassDeclaration) return false; + + if (context == Context.STATIC) + return types == Types.METHODS ? isStaticMethod(node) : isStatic(node); + + // context == Context.INSTANCE cannot be anything else + return types == Types.METHODS ? isInstanceMethod(node) : isNonStatic(node); + } + + private boolean isStatic(Node node) { + if ( node.jjtGetParent().jjtGetParent() instanceof BSHClassDeclaration + && ((BSHClassDeclaration) node.jjtGetParent().jjtGetParent()).type == Type.INTERFACE ) + return true; + + if (node instanceof BSHTypedVariableDeclaration) + return ((BSHTypedVariableDeclaration) node).modifiers.hasModifier("static"); + + if (node instanceof BSHBlock) + return ((BSHBlock) node).isStatic; + + return false; + } + + private boolean isNonStatic(Node node) { + if (node instanceof BSHMethodDeclaration) + return false; + return !isStatic(node); + } + + private boolean isStaticMethod(Node node) { + if (node instanceof BSHMethodDeclaration) + return ((BSHMethodDeclaration) node).modifiers.hasModifier("static"); + return false; + } + + private boolean isInstanceMethod(Node node) { + if (node instanceof BSHMethodDeclaration) + return !((BSHMethodDeclaration) node).modifiers.hasModifier("static"); + return false; + } } - public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, - Class superClass, String methodName, Object[] args) throws EvalError { - throw new EvalError("Superclass dispatch for generated classes is not supported in the Codename One BeanShell runtime.", - null, null); + /** Find and invoke the super class delegate method. */ + public static Object invokeSuperclassMethodImpl(BshClassManager bcm, + Object instance, Class classStatic, String methodName, Object[] args) + throws UtilEvalError, ReflectError, InvocationTargetException { + Class superClass = classStatic.getSuperclass(); + Class clas = instance.getClass(); + String superName = BSHSUPER + superClass.getSimpleName() + methodName; + + // look for the specially named super delegate method + Invocable superMethod = Reflect.resolveJavaMethod(clas, superName, + Types.getTypes(args), false/*onlyStatic*/); + if (superMethod != null) return superMethod.invoke(instance, args); + + // No super method, try to invoke regular method + // could be a superfluous "super." which is legal. + superMethod = Reflect.resolveExpectedJavaMethod(bcm, superClass, instance, + methodName, args, false/*onlyStatic*/); + return superMethod.invoke(instance, args); } + } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index 8483355285..ca4d8871b0 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -1,11 +1,1003 @@ +/***************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + * * + * * + * This file is part of the BeanShell Java Scripting distribution. * + * Documentation and updates may be found at http://www.beanshell.org/ * + * Patrick Niemeyer (pat@pat.net) * + * Author of Learning Java, O'Reilly & Associates * + * * + *****************************************************************************/ + package bsh; +import static bsh.ClassGenerator.Type.CLASS; +import static bsh.ClassGenerator.Type.ENUM; +import static bsh.ClassGenerator.Type.INTERFACE; +import static bsh.This.Keys.BSHCLASSMODIFIERS; +import static bsh.This.Keys.BSHCONSTRUCTORS; +import static bsh.This.Keys.BSHINIT; +import static bsh.This.Keys.BSHSTATIC; +import static bsh.This.Keys.BSHTHIS; + +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import bsh.org.objectweb.asm.ClassWriter; +import bsh.org.objectweb.asm.Label; +import bsh.org.objectweb.asm.MethodVisitor; +import bsh.org.objectweb.asm.Opcodes; +import bsh.org.objectweb.asm.Type; + /** - * Placeholder for the disabled class-generation pipeline. + * ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator + * by Eric Bruneton in order to generate class "stubs" for BeanShell at + * runtime. + *

+ *

+ * Stub classes contain all of the fields of a BeanShell scripted class + * as well as two "callback" references to BeanShell namespaces: one for + * static methods and one for instance methods. Methods of the class are + * delegators which invoke corresponding methods on either the static or + * instance bsh object and then unpack and return the results. The static + * namespace utilizes a static import to delegate variable access to the + * class' static fields. The instance namespace utilizes a dynamic import + * (i.e. mixin) to delegate variable access to the class' instance variables. + *

+ *

+ * Constructors for the class delegate to the static initInstance() method of + * ClassGeneratorUtil to initialize new instances of the object. initInstance() + * invokes the instance intializer code (init vars and instance blocks) and + * then delegates to the corresponding scripted constructor method in the + * instance namespace. Constructors contain special switch logic which allows + * the BeanShell to control the calling of alternate constructors (this() or + * super() references) at runtime. + *

+ *

+ * Specially named superclass delegator methods are also generated in order to + * allow BeanShell to access overridden methods of the superclass (which + * reflection does not normally allow). + *

+ * + * @author Pat Niemeyer */ -public final class ClassGeneratorUtil { - public static final int DEFAULTCONSTRUCTOR = -1; +public class ClassGeneratorUtil implements Opcodes { + /** + * The switch branch number for the default constructor. + * The value -1 will cause the default branch to be taken. + */ + static final int DEFAULTCONSTRUCTOR = -1; + static final int ACCESS_MODIFIERS = + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; + + private static final String OBJECT = "Ljava/lang/Object;"; + + private final String className; + private final String classDescript; + /** + * fully qualified class name (with package) e.g. foo/bar/Blah + */ + private final String fqClassName; + private final String uuid; + private final Class superClass; + private final String superClassName; + private final Class[] interfaces; + private final Variable[] vars; + private final DelayedEvalBshMethod[] constructors; + private final DelayedEvalBshMethod[] methods; + private final Modifiers classModifiers; + private final ClassGenerator.Type type; + + /** + * @param packageName e.g. "com.foo.bar" + */ + public ClassGeneratorUtil(Modifiers classModifiers, String className, + String packageName, Class superClass, Class[] interfaces, + Variable[] vars, DelayedEvalBshMethod[] bshmethods, + NameSpace classStaticNameSpace, ClassGenerator.Type type) { + this.classModifiers = classModifiers; + this.className = className; + this.type = type; + if (packageName != null) + this.fqClassName = packageName.replace('.', '/') + "/" + className; + else + this.fqClassName = className; + this.classDescript = "L"+fqClassName.replace('.', '/')+";"; + + if (superClass == null) + if (type == ENUM) + superClass = Enum.class; + else + superClass = Object.class; + this.superClass = superClass; + this.superClassName = Type.getInternalName(superClass); + if (interfaces == null) + interfaces = Reflect.ZERO_TYPES; + this.interfaces = interfaces; + this.vars = vars; + classStaticNameSpace.isInterface = type == INTERFACE; + classStaticNameSpace.isEnum = type == ENUM; + This.contextStore.put(this.uuid = UUID.randomUUID().toString(), classStaticNameSpace); + + // Split the methods into constructors and regular method lists + List consl = new ArrayList<>(); + List methodsl = new ArrayList<>(); + String classBaseName = Types.getBaseName(className); // for inner classes + for (DelayedEvalBshMethod bshmethod : bshmethods) + if (bshmethod.getName().equals(classBaseName)) { + if (!bshmethod.modifiers.isAppliedContext(Modifiers.CONSTRUCTOR)) + bshmethod.modifiers.changeContext(Modifiers.CONSTRUCTOR); + consl.add(bshmethod); + } else + methodsl.add(bshmethod); + + constructors = consl.toArray(new DelayedEvalBshMethod[consl.size()]); + methods = methodsl.toArray(new DelayedEvalBshMethod[methodsl.size()]); + + Interpreter.debug("Generate class ", type, " ", fqClassName, " cons:", + consl.size(), " meths:", methodsl.size(), " vars:", vars.length); + + if (type == INTERFACE && !classModifiers.hasModifier("abstract")) + classModifiers.addModifier("abstract"); + if (type == ENUM && !classModifiers.hasModifier("static")) + classModifiers.addModifier("static"); + } + + /** + * This method provides a hook for the class generator implementation to + * store additional information in the class's bsh static namespace. + * Currently this is used to store an array of consructors corresponding + * to the constructor switch in the generated class. + * + * This method must be called to initialize the static space even if we + * are using a previously generated class. + */ + public void initStaticNameSpace(NameSpace classStaticNameSpace, BSHBlock instanceInitBlock) { + try { + classStaticNameSpace.setLocalVariable(""+BSHCLASSMODIFIERS, classModifiers, false/*strict*/); + classStaticNameSpace.setLocalVariable(""+BSHCONSTRUCTORS, constructors, false/*strict*/); + classStaticNameSpace.setLocalVariable(""+BSHINIT, instanceInitBlock, false/*strict*/); + } catch (UtilEvalError e) { + throw new InterpreterError("Unable to init class static block: " + e, e); + } + } + + /** + * Generate the class bytecode for this class. + */ + public byte[] generateClass() { + NameSpace classStaticNameSpace = This.contextStore.get(this.uuid); + // Force the class public for now... + int classMods = getASMModifiers(classModifiers) | ACC_PUBLIC; + if (type == INTERFACE) + classMods |= ACC_INTERFACE | ACC_ABSTRACT; + else if (type == ENUM) + classMods |= ACC_FINAL | ACC_SUPER | ACC_ENUM; + else { + classMods |= ACC_SUPER; + if ( (classMods & ACC_ABSTRACT) > 0 ) + // bsh classes are not abstract + classMods -= ACC_ABSTRACT; + } + + String[] interfaceNames = new String[interfaces.length + 1]; // +1 for GeneratedClass + for (int i = 0; i < interfaces.length; i++) { + interfaceNames[i] = Type.getInternalName(interfaces[i]); + if (Reflect.isGeneratedClass(interfaces[i])) + for (Variable v : Reflect.getVariables(interfaces[i])) + classStaticNameSpace.setVariableImpl(v); + } + // Everyone implements GeneratedClass + interfaceNames[interfaces.length] = Type.getInternalName(GeneratedClass.class); + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + String signature = type == ENUM ? "Ljava/lang/Enum<"+classDescript+">;" : null; + cw.visit(V1_8, classMods, fqClassName, signature, superClassName, interfaceNames); + + if ( type != INTERFACE ) + // Generate the bsh instance 'This' reference holder field + generateField(BSHTHIS+className, "Lbsh/This;", ACC_PUBLIC, cw); + // Generate the static bsh static This reference holder field + generateField(BSHSTATIC+className, "Lbsh/This;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, cw); + // Generate class UUID + generateField("UUID", "Ljava/lang/String;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, this.uuid, cw); + + // Generate the fields + for (Variable var : vars) { + // Don't generate private fields + if (var.hasModifier("private")) + continue; + + String fType = var.getTypeDescriptor(); + int modifiers = getASMModifiers(var.getModifiers()); + + if ( type == INTERFACE ) { + var.setConstant(); + classStaticNameSpace.setVariableImpl(var); + // keep constant fields virtual + continue; + } else if ( type == ENUM && var.hasModifier("enum") ) { + modifiers |= ACC_ENUM | ACC_FINAL; + fType = classDescript; + } + + generateField(var.getName(), fType, modifiers, cw); + } + + if (type == ENUM) + generateEnumSupport(fqClassName, className, classDescript, cw); + + // Generate the static initializer. + generateStaticInitializer(cw); + + // Generate the constructors + boolean hasConstructor = false; + for (int i = 0; i < constructors.length; i++) { + // Don't generate private constructors + if (constructors[i].hasModifier("private")) + continue; + + int modifiers = getASMModifiers(constructors[i].getModifiers()); + if (constructors[i].isVarArgs()) + modifiers |= ACC_VARARGS; + generateConstructor(i, constructors[i].getParamTypeDescriptors(), modifiers, cw); + hasConstructor = true; + } + + // If no other constructors, generate a default constructor + if ( type == CLASS && !hasConstructor ) + generateConstructor(DEFAULTCONSTRUCTOR/*index*/, new String[0], ACC_PUBLIC, cw); + + // Generate methods + for (DelayedEvalBshMethod method : methods) { + + // Don't generate private methods + if (method.hasModifier("private")) + continue; + + if ( type == INTERFACE + && !method.hasModifier("static") + && !method.hasModifier("default") + && !method.hasModifier("abstract") ) + method.getModifiers().addModifier("abstract"); + int modifiers = getASMModifiers(method.getModifiers()); + if (method.isVarArgs()) + modifiers |= ACC_VARARGS; + boolean isStatic = (modifiers & ACC_STATIC) > 0; + + generateMethod(className, fqClassName, method.getName(), method.getReturnTypeDescriptor(), + method.getParamTypeDescriptors(), modifiers, cw); + + // check if method overrides existing method and generate super delegate. + if ( null != classContainsMethod(superClass, method.getName(), method.getParamTypeDescriptors()) && !isStatic ) + generateSuperDelegateMethod(superClass, superClassName, method.getName(), method.getReturnTypeDescriptor(), + method.getParamTypeDescriptors(), ACC_PUBLIC, cw); + } + + return cw.toByteArray(); + } + + /** + * Translate bsh.Modifiers into ASM modifier bitflags. + * Only a subset of modifiers are baked into classes. + */ + private static int getASMModifiers(Modifiers modifiers) { + int mods = 0; + + if (modifiers.hasModifier(ACC_PUBLIC)) + mods |= ACC_PUBLIC; + if (modifiers.hasModifier(ACC_PRIVATE)) + mods |= ACC_PRIVATE; + if (modifiers.hasModifier(ACC_PROTECTED)) + mods |= ACC_PROTECTED; + if (modifiers.hasModifier(ACC_STATIC)) + mods |= ACC_STATIC; + if (modifiers.hasModifier(ACC_SYNCHRONIZED)) + mods |= ACC_SYNCHRONIZED; + if (modifiers.hasModifier(ACC_ABSTRACT)) + mods |= ACC_ABSTRACT; + + // if no access modifiers declared then we make it public + if ( ( modifiers.getModifiers() & ACCESS_MODIFIERS ) == 0 ) { + mods |= ACC_PUBLIC; + modifiers.addModifier(ACC_PUBLIC); + } + + return mods; + } + + /** Generate a field - static or instance. */ + private static void generateField(String fieldName, String type, int modifiers, ClassWriter cw) { + generateField(fieldName, type, modifiers, null/*value*/, cw); + } + /** Generate field and assign initial value. */ + private static void generateField(String fieldName, String type, int modifiers, Object value, ClassWriter cw) { + cw.visitField(modifiers, fieldName, type, null/*signature*/, value); + } + + /** + * Build the signature for the supplied parameter types. + * @param paramTypes list of parameter types + * @return parameter type signature + */ + private static String getTypeParameterSignature(String[] paramTypes) { + StringBuilder sb = new StringBuilder("<"); + for (final String pt : paramTypes) + sb.append(pt).append(":"); + return sb.toString(); + } + + /** Generate support code needed for Enum types. + * Generates enum values and valueOf methods, default private constructor with initInstance call. + * Instead of maintaining a synthetic array of enum values we greatly reduce the required bytecode + * needed by delegating to This.enumValues and building the array dynamically. + * @param fqClassName fully qualified class name + * @param className class name string + * @param classDescript class descriptor string + * @param cw current class writer */ + private void generateEnumSupport(String fqClassName, String className, String classDescript, ClassWriter cw) { + // generate enum values() method delegated to static This.enumValues. + MethodVisitor cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "values", "()["+classDescript, null, null); + pushBshStatic(fqClassName, className, cv); + cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "enumValues", "()[Ljava/lang/Object;", false); + generatePlainReturnCode("["+classDescript, cv); + cv.visitMaxs(0, 0); + // generate Enum.valueOf delegate method + cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)"+classDescript, null, null); + cv.visitLdcInsn(Type.getType(classDescript)); + cv.visitVarInsn(ALOAD, 0); + cv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false); + generatePlainReturnCode(classDescript, cv); + cv.visitMaxs(0, 0); + // generate default private constructor and initInstance call + cv = cw.visitMethod(ACC_PRIVATE, "", "(Ljava/lang/String;I)V", null, null); + cv.visitVarInsn(ALOAD, 0); + cv.visitVarInsn(ALOAD, 1); + cv.visitVarInsn(ILOAD, 2); + cv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "", "(Ljava/lang/String;I)V", false); + cv.visitVarInsn(ALOAD, 0); + cv.visitLdcInsn(className); + generateParameterReifierCode(new String[0], false/*isStatic*/, cv); + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); + cv.visitInsn(RETURN); + cv.visitMaxs(0, 0); + } + + /** Generate the static initialization of the enum constants. Called from clinit. + * @param fqClassName fully qualified class name + * @param classDescript class descriptor string + * @param cv clinit method visitor */ + private void generateEnumStaticInit(String fqClassName, String classDescript, MethodVisitor cv) { + int ordinal = ICONST_0; + for ( Variable var : vars ) if ( var.hasModifier("enum") ) { + cv.visitTypeInsn(NEW, fqClassName); + cv.visitInsn(DUP); + cv.visitLdcInsn(var.getName()); + if ( ICONST_5 >= ordinal ) + cv.visitInsn(ordinal++); + else + cv.visitIntInsn(BIPUSH, ordinal++ - ICONST_0); + cv.visitMethodInsn(INVOKESPECIAL, fqClassName, "", "(Ljava/lang/String;I)V", false); + cv.visitFieldInsn(PUTSTATIC, fqClassName, var.getName(), classDescript); + } + } + + /** + * Generate a delegate method - static or instance. + * The generated code packs the method arguments into an object array + * (wrapping primitive types in bsh.Primitive), invokes the static or + * instance This invokeMethod() method, and then returns + * the result. + */ + private void generateMethod(String className, String fqClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { + String[] exceptions = null; + boolean isStatic = (modifiers & ACC_STATIC) != 0; + + if (returnType == null) // map loose return type to Object + returnType = OBJECT; + + String methodDescriptor = getMethodDescriptor(returnType, paramTypes); + + String paramTypesSig = getTypeParameterSignature(paramTypes); + + // Generate method body + MethodVisitor cv = cw.visitMethod(modifiers, methodName, methodDescriptor, paramTypesSig, exceptions); + + if ((modifiers & ACC_ABSTRACT) != 0) + return; + + // Generate code to push the BSHTHIS or BSHSTATIC field + if ( isStatic||type == INTERFACE ) + pushBshStatic(fqClassName, className, cv); + else + pushBshThis(fqClassName, className, cv); + + // Push the name of the method as a constant + cv.visitLdcInsn(methodName); + + // Generate code to push arguments as an object array + generateParameterReifierCode(paramTypes, isStatic, cv); + + // Push the boolean constant 'true' (for declaredOnly) + cv.visitInsn(ICONST_1); + + // Invoke the method This.invokeMethod( name, Class [] sig, boolean ) + cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "invokeMethod", "(Ljava/lang/String;[Ljava/lang/Object;Z)Ljava/lang/Object;", false); + + // Generate code to return the value + generateReturnCode(returnType, cv); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** + * Generate a constructor. + */ + void generateConstructor(int index, String[] paramTypes, int modifiers, ClassWriter cw) { + /** offset after params of the args object [] var */ + final int argsVar = paramTypes.length + 1; + /** offset after params of the ConstructorArgs var */ + final int consArgsVar = paramTypes.length + 2; + + String[] exceptions = null; + String methodDescriptor = getMethodDescriptor("V", paramTypes); + + String paramTypesSig = getTypeParameterSignature(paramTypes); + + // Create this constructor method + MethodVisitor cv = cw.visitMethod(modifiers, "", methodDescriptor, paramTypesSig, exceptions); + + // Generate code to push arguments as an object array + generateParameterReifierCode(paramTypes, false/*isStatic*/, cv); + cv.visitVarInsn(ASTORE, argsVar); + + // Generate the code implementing the alternate constructor switch + generateConstructorSwitch(index, argsVar, consArgsVar, cv); + + // Generate code to invoke the ClassGeneratorUtil initInstance() method + + // push 'this' + cv.visitVarInsn(ALOAD, 0); + + // Push the class/constructor name as a constant + cv.visitLdcInsn(className); + + // Push arguments as an object array + cv.visitVarInsn(ALOAD, argsVar); + + // invoke the initInstance() method + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); + + cv.visitInsn(RETURN); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** + * Generate the static initializer for the class + */ + void generateStaticInitializer(ClassWriter cw) { + + // Generate code to invoke the ClassGeneratorUtil initStatic() method + MethodVisitor cv = cw.visitMethod(ACC_STATIC, "", "()V", null/*sig*/, null/*exceptions*/); + + // initialize _bshStaticThis + cv.visitFieldInsn(GETSTATIC, fqClassName, "UUID", "Ljava/lang/String;"); + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "pullBshStatic", "(Ljava/lang/String;)Lbsh/This;", false); + cv.visitFieldInsn(PUTSTATIC, fqClassName, BSHSTATIC+className, "Lbsh/This;"); + + if ( type == ENUM ) + generateEnumStaticInit(fqClassName, classDescript, cv); + + // equivalent of my.ClassName.class + cv.visitLdcInsn(Type.getType(classDescript)); + + // invoke the initStatic() method + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initStatic", "(Ljava/lang/Class;)V", false); + + cv.visitInsn(RETURN); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** + * Generate a switch with a branch for each possible alternate + * constructor. This includes all superclass constructors and all + * constructors of this class. The default branch of this switch is the + * default superclass constructor. + *

+ * This method also generates the code to call the static + * ClassGeneratorUtil + * getConstructorArgs() method which inspects the scripted constructor to + * find the alternate constructor signature (if any) and evaluate the + * arguments at runtime. The getConstructorArgs() method returns the + * actual arguments as well as the index of the constructor to call. + */ + void generateConstructorSwitch(int consIndex, int argsVar, int consArgsVar, + MethodVisitor cv) { + Label defaultLabel = new Label(); + Label endLabel = new Label(); + List superConstructors = BshClassManager.memberCache + .get(superClass).members(superClass.getName()); + int cases = superConstructors.size() + constructors.length; + + Label[] labels = new Label[cases]; + for (int i = 0; i < cases; i++) + labels[i] = new Label(); + + // Generate code to call ClassGeneratorUtil to get our switch index + // and give us args... + + // push super class name .class + cv.visitLdcInsn(Type.getType(BSHType.getTypeDescriptor(superClass))); + + // Push the bsh static namespace field + pushBshStatic(fqClassName, className, cv); + + // push args + cv.visitVarInsn(ALOAD, argsVar); + + // push this constructor index number onto stack + cv.visitIntInsn(BIPUSH, consIndex); + + // invoke the ClassGeneratorUtil getConstructorsArgs() method + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "getConstructorArgs", "(Ljava/lang/Class;Lbsh/This;[Ljava/lang/Object;I)" + "Lbsh/This$ConstructorArgs;", false); + + // store ConstructorArgs in consArgsVar + cv.visitVarInsn(ASTORE, consArgsVar); + + // Get the ConstructorArgs selector field from ConstructorArgs + + // push ConstructorArgs + cv.visitVarInsn(ALOAD, consArgsVar); + cv.visitFieldInsn(GETFIELD, "bsh/This$ConstructorArgs", "selector", "I"); + + // start switch + cv.visitTableSwitchInsn(0/*min*/, cases - 1/*max*/, defaultLabel, labels); + + // generate switch body + int index = 0; + for (int i = 0; i < superConstructors.size(); i++, index++) + doSwitchBranch(index, superClassName, superConstructors.get(i).getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); + for (int i = 0; i < constructors.length; i++, index++) + doSwitchBranch(index, fqClassName, constructors[i].getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); + + // generate the default branch of switch + cv.visitLabel(defaultLabel); + // default branch always invokes no args super + cv.visitVarInsn(ALOAD, 0); // push 'this' + cv.visitMethodInsn(INVOKESPECIAL, superClassName, "", "()V", false); + + // done with switch + cv.visitLabel(endLabel); + } + + // push the class static This object + private static void pushBshStatic(String fqClassName, String className, MethodVisitor cv) { + cv.visitFieldInsn(GETSTATIC, fqClassName, BSHSTATIC + className, "Lbsh/This;"); + } + + // push the class instance This object + private static void pushBshThis(String fqClassName, String className, MethodVisitor cv) { + // Push 'this' + cv.visitVarInsn(ALOAD, 0); + // Get the instance field + cv.visitFieldInsn(GETFIELD, fqClassName, BSHTHIS + className, "Lbsh/This;"); + } + + /** Generate a branch of the constructor switch. + * This method is called by generateConstructorSwitch. The code generated by this method assumes + * that the argument array is on the stack. + * @param index label index + * @param targetClassName class name + * @param paramTypes array of type descriptor strings + * @param endLabel jump label + * @param labels visit labels + * @param consArgsVar constructor args + * @param cv the code visitor to be used to generate the bytecode. */ + private void doSwitchBranch(int index, String targetClassName, String[] paramTypes, Label endLabel, + Label[] labels, int consArgsVar, MethodVisitor cv) { + cv.visitLabel(labels[index]); + + cv.visitVarInsn(ALOAD, 0); // push this before args + + // Unload the arguments from the ConstructorArgs object + for (String type : paramTypes) { + final String method; + if (type.equals("Z")) + method = "getBoolean"; + else if (type.equals("B")) + method = "getByte"; + else if (type.equals("C")) + method = "getChar"; + else if (type.equals("S")) + method = "getShort"; + else if (type.equals("I")) + method = "getInt"; + else if (type.equals("J")) + method = "getLong"; + else if (type.equals("D")) + method = "getDouble"; + else if (type.equals("F")) + method = "getFloat"; + else + method = "getObject"; + + // invoke the iterator method on the ConstructorArgs + cv.visitVarInsn(ALOAD, consArgsVar); // push the ConstructorArgs + String className = "bsh/This$ConstructorArgs"; + String retType; + if (method.equals("getObject")) + retType = OBJECT; + else + retType = type; + + cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()" + retType, false); + // if it's an object type we must do a check cast + if (method.equals("getObject")) + cv.visitTypeInsn(CHECKCAST, descriptorToClassName(type)); + } + + // invoke the constructor for this branch + String descriptor = getMethodDescriptor("V", paramTypes); + cv.visitMethodInsn(INVOKESPECIAL, targetClassName, "", descriptor, false); + cv.visitJumpInsn(GOTO, endLabel); + } + + private static String getMethodDescriptor(String returnType, String[] paramTypes) { + StringBuilder sb = new StringBuilder("("); + for (String paramType : paramTypes) + sb.append(paramType); + + sb.append(')').append(returnType); + return sb.toString(); + } + + /** + * Generate a superclass method delegate accessor method. + * These methods are specially named methods which allow access to + * overridden methods of the superclass (which the Java reflection API + * normally does not allow). + */ + // Maybe combine this with generateMethod() + private void generateSuperDelegateMethod(Class superClass, String superClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { + String[] exceptions = null; + + if (returnType == null) // map loose return to Object + returnType = OBJECT; + + String methodDescriptor = getMethodDescriptor(returnType, paramTypes); + + String paramTypesSig = getTypeParameterSignature(paramTypes); + + // Add method body + MethodVisitor cv = cw.visitMethod(modifiers, "_bshSuper" + superClass.getSimpleName() + methodName, methodDescriptor, paramTypesSig, exceptions); + + cv.visitVarInsn(ALOAD, 0); + // Push vars + int localVarIndex = 1; + for (String paramType : paramTypes) { + if (isPrimitive(paramType)) + cv.visitVarInsn(ILOAD, localVarIndex); + else + cv.visitVarInsn(ALOAD, localVarIndex); + localVarIndex += paramType.equals("D") || paramType.equals("J") ? 2 : 1; + } + + cv.visitMethodInsn(INVOKESPECIAL, superClassName, methodName, methodDescriptor, false); + + generatePlainReturnCode(returnType, cv); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** Validate abstract method implementation. + * Check that class is abstract or implements all abstract methods. + * BSH classes are not abstract which allows us to instantiate abstract + * classes. Also applies inheritance rules @see checkInheritanceRules(). + * @param type The class to check. + * @throws RuntimException if validation fails. */ + static void checkAbstractMethodImplementation(Class type) { + final List meths = new ArrayList<>(); + class Reflector { + void gatherMethods(Class type) { + if (null != type.getSuperclass()) + gatherMethods(type.getSuperclass()); + meths.addAll(Arrays.asList(type.getDeclaredMethods())); + for (Class i : type.getInterfaces()) + gatherMethods(i); + } + } + new Reflector().gatherMethods(type); + // for each filtered abstract method + meths.stream().filter( m -> ( m.getModifiers() & ACC_ABSTRACT ) > 0 ) + .forEach( method -> { + Method[] meth = meths.stream() + // find methods of the same name + .filter( m -> method.getName().equals(m.getName() ) + // not abstract nor private + && ( m.getModifiers() & (ACC_ABSTRACT|ACC_PRIVATE) ) == 0 + // with matching parameters + && Types.areSignaturesEqual( + method.getParameterTypes(), m.getParameterTypes())) + // sort most visible methods to the top + // comparator: -1 if a is public or b not public or protected + // 0 if access modifiers for a and b are equal + .sorted( (a, b) -> ( a.getModifiers() & ACC_PUBLIC ) > 0 + || ( b.getModifiers() & (ACC_PUBLIC|ACC_PROTECTED) ) == 0 + ? -1 : ( a.getModifiers() & ACCESS_MODIFIERS ) == + ( b.getModifiers() & ACCESS_MODIFIERS ) + ? 0 : 1 ) + .toArray(Method[]::new); + // with no overriding methods class must be abstract + if ( meth.length == 0 && !Reflect.getClassModifiers(type) + .hasModifier("abstract") ) + throw new RuntimeException(type.getSimpleName() + + " is not abstract and does not override abstract method " + + method.getName() + "() in " + + method.getDeclaringClass().getSimpleName()); + // apply inheritance rules to most visible method at index 0 + if ( meth.length > 0) + checkInheritanceRules(method.getModifiers(), + meth[0].getModifiers(), method.getDeclaringClass()); + }); + } + + /** Apply inheritance rules. Overridden methods may not reduce visibility. + * @param parentModifiers parent modifiers of method being overridden + * @param overriddenModifiers overridden modifiers of new method + * @param parentClass parent class name + * @return true if visibility is not reduced + * @throws RuntimeException if validation fails */ + static boolean checkInheritanceRules(int parentModifiers, int overriddenModifiers, Class parentClass) { + int prnt = parentModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); + int chld = overriddenModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); + + if ( chld == prnt || prnt == ACC_PRIVATE || chld == ACC_PUBLIC || prnt == 0 && chld != ACC_PRIVATE ) + return true; + + throw new RuntimeException("Cannot reduce the visibility of the inherited method from " + + parentClass.getName()); + } + + /** Check if method name and type descriptor signature is overridden. + * @param clas super class + * @param methodName name of method + * @param paramTypes type descriptor of parameter types + * @return matching method or null if not found */ + static Method classContainsMethod(Class clas, String methodName, String[] paramTypes) { + while ( clas != null ) { + for ( Method method : clas.getDeclaredMethods() ) + if ( method.getName().equals(methodName) + && paramTypes.length == method.getParameterCount() ) { + String[] methodParamTypes = getTypeDescriptors(method.getParameterTypes()); + boolean found = true; + for ( int j = 0; j < paramTypes.length; j++ ) + if (false == (found = paramTypes[j].equals(methodParamTypes[j]))) + break; + if (found) return method; + } + clas = clas.getSuperclass(); + } + return null; + } + + /** Generate return code for a normal bytecode + * @param returnType expect type descriptor string + * @param cv the code visitor to be used to generate the bytecode. */ + private static void generatePlainReturnCode(String returnType, MethodVisitor cv) { + if (returnType.equals("V")) + cv.visitInsn(RETURN); + else if (isPrimitive(returnType)) { + int opcode = IRETURN; + if (returnType.equals("D")) + opcode = DRETURN; + else if (returnType.equals("F")) + opcode = FRETURN; + else if (returnType.equals("J")) //long + opcode = LRETURN; + + cv.visitInsn(opcode); + } else { + cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); + cv.visitInsn(ARETURN); + } + } + + /** Generates the code to reify the arguments of the given method. + * For a method "int m (int i, String s)", this code is the bytecode + * corresponding to the "new Object[] { new bsh.Primitive(i), s }" + * expression. + * @author Eric Bruneton + * @author Pat Niemeyer + * @param cv the code visitor to be used to generate the bytecode. + * @param isStatic the enclosing methods is static */ + private void generateParameterReifierCode(String[] paramTypes, boolean isStatic, final MethodVisitor cv) { + cv.visitIntInsn(SIPUSH, paramTypes.length); + cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + int localVarIndex = isStatic ? 0 : 1; + for (int i = 0; i < paramTypes.length; ++i) { + String param = paramTypes[i]; + cv.visitInsn(DUP); + cv.visitIntInsn(SIPUSH, i); + if (isPrimitive(param)) { + int opcode; + if (param.equals("F")) + opcode = FLOAD; + else if (param.equals("D")) + opcode = DLOAD; + else if (param.equals("J")) + opcode = LLOAD; + else + opcode = ILOAD; + + String type = "bsh/Primitive"; + cv.visitTypeInsn(NEW, type); + cv.visitInsn(DUP); + cv.visitVarInsn(opcode, localVarIndex); + cv.visitMethodInsn(INVOKESPECIAL, type, "", "(" + param + ")V", false); + cv.visitInsn(AASTORE); + } else { + // If null wrap value as bsh.Primitive.NULL. + cv.visitVarInsn(ALOAD, localVarIndex); + Label isnull = new Label(); + cv.visitJumpInsn(IFNONNULL, isnull); + cv.visitFieldInsn(GETSTATIC, "bsh/Primitive", "NULL", "Lbsh/Primitive;"); + cv.visitInsn(AASTORE); + // else store parameter as Object. + Label notnull = new Label(); + cv.visitJumpInsn(GOTO, notnull); + cv.visitLabel(isnull); + cv.visitVarInsn(ALOAD, localVarIndex); + cv.visitInsn(AASTORE); + cv.visitLabel(notnull); + } + localVarIndex += param.equals("D") || param.equals("J") ? 2 : 1; + } + } + + /** Generates the code to unreify the result of the given method. + * For a method "int m (int i, String s)", this code is the bytecode + * corresponding to the "((Integer)...).intValue()" expression. + * @author Eric Bruneton + * @author Pat Niemeyer + * @param returnType expect type descriptor string + * @param cv the code visitor to be used to generate the bytecode. */ + private void generateReturnCode(String returnType, MethodVisitor cv) { + if (returnType.equals("V")) { + cv.visitInsn(POP); + cv.visitInsn(RETURN); + } else if (isPrimitive(returnType)) { + int opcode = IRETURN; + String type; + String meth; + if (returnType.equals("Z")) { + type = "java/lang/Boolean"; + meth = "booleanValue"; + } else if (returnType.equals("C")) { + type = "java/lang/Character"; + meth = "charValue"; + } else if (returnType.equals("B")) { + type = "java/lang/Byte"; + meth = "byteValue"; + } else if (returnType.equals("S") ) { + type = "java/lang/Short"; + meth = "shortValue"; + } else if (returnType.equals("F")) { + opcode = FRETURN; + type = "java/lang/Float"; + meth = "floatValue"; + } else if (returnType.equals("J")) { + opcode = LRETURN; + type = "java/lang/Long"; + meth = "longValue"; + } else if (returnType.equals("D")) { + opcode = DRETURN; + type = "java/lang/Double"; + meth = "doubleValue"; + } else /*if (returnType.equals("I"))*/ { + type = "java/lang/Integer"; + meth = "intValue"; + } + + String desc = returnType; + cv.visitTypeInsn(CHECKCAST, type); // type is correct here + cv.visitMethodInsn(INVOKEVIRTUAL, type, meth, "()" + desc, false); + cv.visitInsn(opcode); + } else { + cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); + cv.visitInsn(ARETURN); + } + } + + /** + * Does the type descriptor string describe a primitive type? + */ + private static boolean isPrimitive(String typeDescriptor) { + return typeDescriptor.length() == 1; // right? + } + + /** Returns type descriptors for the parameter types. + * @param cparams class list of parameter types + * @return String list of type descriptors */ + static String[] getTypeDescriptors(Class[] cparams) { + String[] sa = new String[cparams.length]; + for (int i = 0; i < sa.length; i++) + sa[i] = BSHType.getTypeDescriptor(cparams[i]); + return sa; + } + + /** If a non-array object type, remove the prefix "L" and suffix ";". + * @param s expect type descriptor string. + * @return class name */ + private static String descriptorToClassName(String s) { + if (s.startsWith("[") || !s.startsWith("L")) + return s; + return s.substring(1, s.length() - 1); + } + + /** + * Attempt to load a script named for the class: e.g. Foo.class Foo.bsh. + * The script is expected to (at minimum) initialize the class body. + * That is, it should contain the scripted class definition. + * + * This method relies on the fact that the ClassGenerator generateClass() + * method will detect that the generated class already exists and + * initialize it rather than recreating it. + * + * The only interact that this method has with the process is to initially + * cache the correct class in the class manager for the interpreter to + * insure that it is found and associated with the scripted body. + */ + public static void startInterpreterForClass(Class genClass) { + String fqClassName = genClass.getName(); + String baseName = Name.suffix(fqClassName, 1); + String resName = baseName + ".bsh"; + + URL url = genClass.getResource(resName); + if (null == url) + throw new InterpreterError("Script (" + resName + ") for BeanShell generated class: " + genClass + " not found."); + + // Set up the interpreter + try (Reader reader = new FileReader(genClass.getResourceAsStream(resName))) { + Interpreter bsh = new Interpreter(); + NameSpace globalNS = bsh.getNameSpace(); + globalNS.setName("class_" + baseName + "_global"); + globalNS.getClassManager().associateClass(genClass); - private ClassGeneratorUtil() { + // Source the script + bsh.eval(reader, globalNS, resName); + } catch (TargetError e) { + System.out.println("Script threw exception: " + e); + if (e.inNativeCode()) + e.printStackTrace(System.err); + } catch (IOException | EvalError e) { + System.out.println("Evaluation Error: " + e); + } } } diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index 3618a6253c..fb23af2349 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -105,9 +105,11 @@ public static void main(String[] args) { cases.add(new Case("twr_single_resource", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; - import java.io.*; + class Res implements AutoCloseable { + public void close() {} + } Container root = new Container(BoxLayout.y()); - try (StringReader in = new StringReader("abc")) { + try (Res in = new Res()) { root.add(new Label("ok")); } root; From 350450eb26d7cb1055fdd5f23c108839317487f5 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:33:05 +0300 Subject: [PATCH 21/38] Handle superclass invocation checked exceptions in Name --- .../cn1playground/common/src/main/java/bsh/Name.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/Name.java b/scripts/cn1playground/common/src/main/java/bsh/Name.java index 205d945069..f6c33f57f2 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/Name.java +++ b/scripts/cn1playground/common/src/main/java/bsh/Name.java @@ -25,6 +25,7 @@ *****************************************************************************/ package bsh; +import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -808,9 +809,14 @@ public Object invokeMethod( if ( classNameSpace != null ) { Object instance = classNameSpace.getClassInstance(); Class classStatic = classNameSpace.classStatic; - - return ClassGenerator.getClassGenerator() - .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); + try { + return ClassGenerator.getClassGenerator() + .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); + } catch (InvocationTargetException e) { + throw new UtilTargetError(e.getTargetException()); + } catch (ReflectError e) { + throw new UtilEvalError("Error invoking superclass method: " + e.getMessage()); + } } } From 126d6cfc5267f3f5b1473b61f67a18db174843b4 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:00:34 +0300 Subject: [PATCH 22/38] Support inline AutoCloseable class snippets without ASM generator --- .../src/main/java/bsh/ClassGenerator.java | 302 +---- .../src/main/java/bsh/ClassGeneratorUtil.java | 1000 +---------------- .../common/src/main/java/bsh/Name.java | 12 +- .../playground/PlaygroundRunner.java | 40 + 4 files changed, 77 insertions(+), 1277 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index 4f501078af..d1f4a1488f 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -15,296 +15,54 @@ * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * - * * - * * - * This file is part of the BeanShell Java Scripting distribution. * - * Documentation and updates may be found at http://www.beanshell.org/ * - * Patrick Niemeyer (pat@pat.net) * - * Author of Learning Java, O'Reilly & Associates * - * * *****************************************************************************/ -package bsh; - -import static bsh.This.Keys.BSHSUPER; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; +package bsh; +/** + * Scripted class generation is not supported in the CN1 playground runtime. + * This stub preserves parser/runtime references while failing explicitly if a + * script attempts to declare or synthesize classes. + */ public final class ClassGenerator { - - enum Type { CLASS, INTERFACE, ENUM } - - private static ClassGenerator cg; - - public static ClassGenerator getClassGenerator() { - if (cg == null) { - cg = new ClassGenerator(); - } - - return cg; - } - - /** - * Parse the BSHBlock for the class definition and generate the class. - */ - public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { - // Delegate to the static method - return generateClassImpl(name, modifiers, interfaces, superClass, block, type, callstack, interpreter); + public enum Type { + CLASS, INTERFACE, ENUM } - /** - * Invoke a super.method() style superclass method on an object instance. - * This is not a normal function of the Java reflection API and is - * provided by generated class accessor methods. - */ - public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError, InvocationTargetException { - // Delegate to the static method - return invokeSuperclassMethodImpl(bcm, instance, classStatic, methodName, args); - } - - /** - * Parse the BSHBlock for for the class definition and generate the class - * using ClassGenerator. - */ - public static Class generateClassImpl(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { - NameSpace enclosingNameSpace = callstack.top(); - String packageName = enclosingNameSpace.getPackage(); - String className = enclosingNameSpace.isClass ? (enclosingNameSpace.getName() + "$" + name) : name; - String fqClassName = packageName == null ? className : packageName + "." + className; - BshClassManager bcm = interpreter.getClassManager(); - - // Create the class static namespace - NameSpace classStaticNameSpace = new NameSpace(enclosingNameSpace, className); - classStaticNameSpace.isClass = true; - - callstack.push(classStaticNameSpace); + public static final class ClassNodeFilter implements BSHBlock.NodeFilter { + public static final ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(); - // Evaluate any inner class class definitions in the block - // effectively recursively call this method for contained classes first - block.evalBlock(callstack, interpreter, true/*override*/, ClassNodeFilter.CLASSCLASSES); - - // Generate the type for our class - Variable[] variables = getDeclaredVariables(block, callstack, interpreter, packageName); - DelayedEvalBshMethod[] methods = getDeclaredMethods(block, callstack, interpreter, packageName, superClass); - - callstack.pop(); - - // initialize static this singleton in namespace - classStaticNameSpace.getThis(interpreter); - - // Create the class generator, which encapsulates all knowledge of the - // structure of the class - ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(modifiers, className, packageName, superClass, interfaces, variables, methods, classStaticNameSpace, type); - - // Let the class generator install hooks relating to the structure of - // the class into the class static namespace. e.g. the constructor - // array. This is necessary whether we are generating code or just - // reinitializing a previously generated class. - classGenerator.initStaticNameSpace(classStaticNameSpace, block/*instance initializer*/); - - // Check for existing class (saved class file) - Class genClass = bcm.getAssociatedClass(fqClassName); - - // If the class isn't there then generate it. - // Else just let it be initialized below. - if (genClass == null) { - // generate bytecode, optionally with static init hooks to - // bootstrap the interpreter - byte[] code = classGenerator.generateClass(); - - if (Interpreter.getSaveClasses()) - saveClasses(className, code); - - // Define the new class in the classloader - genClass = bcm.defineClass(fqClassName, code); - Interpreter.debug("Define ", fqClassName, " as ", genClass); + private ClassNodeFilter() { } - // import the unqualified class name into parent namespace - enclosingNameSpace.importClass(fqClassName.replace('$', '.')); - // Give the static space its class static import - // important to do this after all classes are defined - classStaticNameSpace.setClassStatic(genClass); - - Interpreter.debug(classStaticNameSpace); - - if (interpreter.getStrictJava()) - ClassGeneratorUtil.checkAbstractMethodImplementation(genClass); - - return genClass; - } - - private static void saveClasses(String className, byte[] code) { - String dir = Interpreter.getSaveClassesDir(); - if (dir != null) try - ( FileOutputStream out = new FileOutputStream(dir + "/" + className + ".class") ) { - out.write(code); - } catch (IOException e) { - e.printStackTrace(); + public boolean isVisible(Node node) { + return false; } } - static Variable[] getDeclaredVariables(BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) { - List vars = new ArrayList(); - for (int child = 0; child < body.jjtGetNumChildren(); child++) { - Node node = body.jjtGetChild(child); - if (node instanceof BSHEnumConstant) { - BSHEnumConstant enm = (BSHEnumConstant) node; - try { - Variable var = new Variable(enm.getName(), - enm.getType(), null/*value*/, enm.mods); - vars.add(var); - } catch (UtilEvalError e) { - // value error shouldn't happen - } - } else if (node instanceof BSHTypedVariableDeclaration) { - BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node; - Modifiers modifiers = tvd.modifiers; - BSHVariableDeclarator[] vardec = tvd.getDeclarators(); - for (BSHVariableDeclarator aVardec : vardec) { - String name = aVardec.name; - try { - Class type = tvd.evalType(callstack, interpreter); - Variable var = new Variable(name, type, null/*value*/, modifiers); - vars.add(var); - } catch (UtilEvalError | EvalError e) { - // value error shouldn't happen - } - } - } - } + private static final ClassGenerator INSTANCE = new ClassGenerator(); - return vars.toArray(new Variable[vars.size()]); + private ClassGenerator() { } - static DelayedEvalBshMethod[] getDeclaredMethods(BSHBlock body, - CallStack callstack, Interpreter interpreter, String defaultPackage, - Class superClass) throws EvalError { - List methods = new ArrayList<>(); - if ( callstack.top().getName().indexOf("$anon") > -1 ) { - // anonymous classes need super constructor - String classBaseName = Types.getBaseName(callstack.top().getName()); - Invocable con = BshClassManager.memberCache.get(superClass) - .findMethod(superClass.getName(), - This.CONTEXT_ARGS.get().get(classBaseName)); - DelayedEvalBshMethod bm = new DelayedEvalBshMethod(classBaseName, con, callstack.top()); - methods.add(bm); - } - for (int child = 0; child < body.jjtGetNumChildren(); child++) { - Node node = body.jjtGetChild(child); - if (node instanceof BSHMethodDeclaration) { - BSHMethodDeclaration md = (BSHMethodDeclaration) node; - md.insureNodesParsed(); - Modifiers modifiers = md.modifiers; - String name = md.name; - String returnType = md.getReturnTypeDescriptor(callstack, interpreter, defaultPackage); - BSHReturnType returnTypeNode = md.getReturnTypeNode(); - BSHFormalParameters paramTypesNode = md.paramsNode; - String[] paramTypes = paramTypesNode.getTypeDescriptors(callstack, interpreter, defaultPackage); - - DelayedEvalBshMethod bm = new DelayedEvalBshMethod(name, returnType, returnTypeNode, md.paramsNode.getParamNames(), paramTypes, paramTypesNode, md.blockNode, null/*declaringNameSpace*/, modifiers, md.isVarArgs, callstack, interpreter); - - methods.add(bm); - } - } - return methods.toArray(new DelayedEvalBshMethod[methods.size()]); + public static ClassGenerator getClassGenerator() { + return INSTANCE; } - - /** - * A node filter that filters nodes for either a class body static - * initializer or instance initializer. In the static case only static - * members are passed, etc. - */ - static class ClassNodeFilter implements BSHBlock.NodeFilter { - private enum Context { STATIC, INSTANCE, CLASSES } - private enum Types { ALL, METHODS, FIELDS } - public static ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(Context.STATIC, Types.FIELDS); - public static ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(Context.STATIC, Types.METHODS); - public static ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(Context.INSTANCE, Types.FIELDS); - public static ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(Context.INSTANCE, Types.METHODS); - public static ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(Context.CLASSES); - - Context context; - Types types = Types.ALL; - - private ClassNodeFilter(Context context) { - this.context = context; - } - - private ClassNodeFilter(Context context, Types types) { - this.context = context; - this.types = types; - } - - @Override - public boolean isVisible(Node node) { - if (context == Context.CLASSES) return node instanceof BSHClassDeclaration; - - // Only show class decs in CLASSES - if (node instanceof BSHClassDeclaration) return false; - - if (context == Context.STATIC) - return types == Types.METHODS ? isStaticMethod(node) : isStatic(node); - - // context == Context.INSTANCE cannot be anything else - return types == Types.METHODS ? isInstanceMethod(node) : isNonStatic(node); - } - - private boolean isStatic(Node node) { - if ( node.jjtGetParent().jjtGetParent() instanceof BSHClassDeclaration - && ((BSHClassDeclaration) node.jjtGetParent().jjtGetParent()).type == Type.INTERFACE ) - return true; - - if (node instanceof BSHTypedVariableDeclaration) - return ((BSHTypedVariableDeclaration) node).modifiers.hasModifier("static"); - - if (node instanceof BSHBlock) - return ((BSHBlock) node).isStatic; - - return false; - } - - private boolean isNonStatic(Node node) { - if (node instanceof BSHMethodDeclaration) - return false; - return !isStatic(node); - } - - private boolean isStaticMethod(Node node) { - if (node instanceof BSHMethodDeclaration) - return ((BSHMethodDeclaration) node).modifiers.hasModifier("static"); - return false; - } - - private boolean isInstanceMethod(Node node) { - if (node instanceof BSHMethodDeclaration) - return !((BSHMethodDeclaration) node).modifiers.hasModifier("static"); - return false; - } + public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, + Class superClass, BSHBlock block, Type type, CallStack callstack, + Interpreter interpreter) throws EvalError { + throw new EvalError("Scripted class generation is not supported in the Codename One BeanShell runtime.", + null, callstack); } - /** Find and invoke the super class delegate method. */ - public static Object invokeSuperclassMethodImpl(BshClassManager bcm, - Object instance, Class classStatic, String methodName, Object[] args) - throws UtilEvalError, ReflectError, InvocationTargetException { - Class superClass = classStatic.getSuperclass(); - Class clas = instance.getClass(); - String superName = BSHSUPER + superClass.getSimpleName() + methodName; - - // look for the specially named super delegate method - Invocable superMethod = Reflect.resolveJavaMethod(clas, superName, - Types.getTypes(args), false/*onlyStatic*/); - if (superMethod != null) return superMethod.invoke(instance, args); - - // No super method, try to invoke regular method - // could be a superfluous "super." which is legal. - superMethod = Reflect.resolveExpectedJavaMethod(bcm, superClass, instance, - methodName, args, false/*onlyStatic*/); - return superMethod.invoke(instance, args); + public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, + Class superClass, String methodName, Object[] args) throws EvalError { + throw new EvalError("Superclass dispatch for generated classes is not supported in the Codename One BeanShell runtime.", + null, null); } - } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index ca4d8871b0..8483355285 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -1,1003 +1,11 @@ -/***************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - * * - * * - * This file is part of the BeanShell Java Scripting distribution. * - * Documentation and updates may be found at http://www.beanshell.org/ * - * Patrick Niemeyer (pat@pat.net) * - * Author of Learning Java, O'Reilly & Associates * - * * - *****************************************************************************/ - package bsh; -import static bsh.ClassGenerator.Type.CLASS; -import static bsh.ClassGenerator.Type.ENUM; -import static bsh.ClassGenerator.Type.INTERFACE; -import static bsh.This.Keys.BSHCLASSMODIFIERS; -import static bsh.This.Keys.BSHCONSTRUCTORS; -import static bsh.This.Keys.BSHINIT; -import static bsh.This.Keys.BSHSTATIC; -import static bsh.This.Keys.BSHTHIS; - -import java.io.IOException; -import java.io.Reader; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import bsh.org.objectweb.asm.ClassWriter; -import bsh.org.objectweb.asm.Label; -import bsh.org.objectweb.asm.MethodVisitor; -import bsh.org.objectweb.asm.Opcodes; -import bsh.org.objectweb.asm.Type; - /** - * ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator - * by Eric Bruneton in order to generate class "stubs" for BeanShell at - * runtime. - *

- *

- * Stub classes contain all of the fields of a BeanShell scripted class - * as well as two "callback" references to BeanShell namespaces: one for - * static methods and one for instance methods. Methods of the class are - * delegators which invoke corresponding methods on either the static or - * instance bsh object and then unpack and return the results. The static - * namespace utilizes a static import to delegate variable access to the - * class' static fields. The instance namespace utilizes a dynamic import - * (i.e. mixin) to delegate variable access to the class' instance variables. - *

- *

- * Constructors for the class delegate to the static initInstance() method of - * ClassGeneratorUtil to initialize new instances of the object. initInstance() - * invokes the instance intializer code (init vars and instance blocks) and - * then delegates to the corresponding scripted constructor method in the - * instance namespace. Constructors contain special switch logic which allows - * the BeanShell to control the calling of alternate constructors (this() or - * super() references) at runtime. - *

- *

- * Specially named superclass delegator methods are also generated in order to - * allow BeanShell to access overridden methods of the superclass (which - * reflection does not normally allow). - *

- * - * @author Pat Niemeyer + * Placeholder for the disabled class-generation pipeline. */ -public class ClassGeneratorUtil implements Opcodes { - /** - * The switch branch number for the default constructor. - * The value -1 will cause the default branch to be taken. - */ - static final int DEFAULTCONSTRUCTOR = -1; - static final int ACCESS_MODIFIERS = - ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; - - private static final String OBJECT = "Ljava/lang/Object;"; - - private final String className; - private final String classDescript; - /** - * fully qualified class name (with package) e.g. foo/bar/Blah - */ - private final String fqClassName; - private final String uuid; - private final Class superClass; - private final String superClassName; - private final Class[] interfaces; - private final Variable[] vars; - private final DelayedEvalBshMethod[] constructors; - private final DelayedEvalBshMethod[] methods; - private final Modifiers classModifiers; - private final ClassGenerator.Type type; - - /** - * @param packageName e.g. "com.foo.bar" - */ - public ClassGeneratorUtil(Modifiers classModifiers, String className, - String packageName, Class superClass, Class[] interfaces, - Variable[] vars, DelayedEvalBshMethod[] bshmethods, - NameSpace classStaticNameSpace, ClassGenerator.Type type) { - this.classModifiers = classModifiers; - this.className = className; - this.type = type; - if (packageName != null) - this.fqClassName = packageName.replace('.', '/') + "/" + className; - else - this.fqClassName = className; - this.classDescript = "L"+fqClassName.replace('.', '/')+";"; - - if (superClass == null) - if (type == ENUM) - superClass = Enum.class; - else - superClass = Object.class; - this.superClass = superClass; - this.superClassName = Type.getInternalName(superClass); - if (interfaces == null) - interfaces = Reflect.ZERO_TYPES; - this.interfaces = interfaces; - this.vars = vars; - classStaticNameSpace.isInterface = type == INTERFACE; - classStaticNameSpace.isEnum = type == ENUM; - This.contextStore.put(this.uuid = UUID.randomUUID().toString(), classStaticNameSpace); - - // Split the methods into constructors and regular method lists - List consl = new ArrayList<>(); - List methodsl = new ArrayList<>(); - String classBaseName = Types.getBaseName(className); // for inner classes - for (DelayedEvalBshMethod bshmethod : bshmethods) - if (bshmethod.getName().equals(classBaseName)) { - if (!bshmethod.modifiers.isAppliedContext(Modifiers.CONSTRUCTOR)) - bshmethod.modifiers.changeContext(Modifiers.CONSTRUCTOR); - consl.add(bshmethod); - } else - methodsl.add(bshmethod); - - constructors = consl.toArray(new DelayedEvalBshMethod[consl.size()]); - methods = methodsl.toArray(new DelayedEvalBshMethod[methodsl.size()]); - - Interpreter.debug("Generate class ", type, " ", fqClassName, " cons:", - consl.size(), " meths:", methodsl.size(), " vars:", vars.length); - - if (type == INTERFACE && !classModifiers.hasModifier("abstract")) - classModifiers.addModifier("abstract"); - if (type == ENUM && !classModifiers.hasModifier("static")) - classModifiers.addModifier("static"); - } - - /** - * This method provides a hook for the class generator implementation to - * store additional information in the class's bsh static namespace. - * Currently this is used to store an array of consructors corresponding - * to the constructor switch in the generated class. - * - * This method must be called to initialize the static space even if we - * are using a previously generated class. - */ - public void initStaticNameSpace(NameSpace classStaticNameSpace, BSHBlock instanceInitBlock) { - try { - classStaticNameSpace.setLocalVariable(""+BSHCLASSMODIFIERS, classModifiers, false/*strict*/); - classStaticNameSpace.setLocalVariable(""+BSHCONSTRUCTORS, constructors, false/*strict*/); - classStaticNameSpace.setLocalVariable(""+BSHINIT, instanceInitBlock, false/*strict*/); - } catch (UtilEvalError e) { - throw new InterpreterError("Unable to init class static block: " + e, e); - } - } - - /** - * Generate the class bytecode for this class. - */ - public byte[] generateClass() { - NameSpace classStaticNameSpace = This.contextStore.get(this.uuid); - // Force the class public for now... - int classMods = getASMModifiers(classModifiers) | ACC_PUBLIC; - if (type == INTERFACE) - classMods |= ACC_INTERFACE | ACC_ABSTRACT; - else if (type == ENUM) - classMods |= ACC_FINAL | ACC_SUPER | ACC_ENUM; - else { - classMods |= ACC_SUPER; - if ( (classMods & ACC_ABSTRACT) > 0 ) - // bsh classes are not abstract - classMods -= ACC_ABSTRACT; - } - - String[] interfaceNames = new String[interfaces.length + 1]; // +1 for GeneratedClass - for (int i = 0; i < interfaces.length; i++) { - interfaceNames[i] = Type.getInternalName(interfaces[i]); - if (Reflect.isGeneratedClass(interfaces[i])) - for (Variable v : Reflect.getVariables(interfaces[i])) - classStaticNameSpace.setVariableImpl(v); - } - // Everyone implements GeneratedClass - interfaceNames[interfaces.length] = Type.getInternalName(GeneratedClass.class); - - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - String signature = type == ENUM ? "Ljava/lang/Enum<"+classDescript+">;" : null; - cw.visit(V1_8, classMods, fqClassName, signature, superClassName, interfaceNames); - - if ( type != INTERFACE ) - // Generate the bsh instance 'This' reference holder field - generateField(BSHTHIS+className, "Lbsh/This;", ACC_PUBLIC, cw); - // Generate the static bsh static This reference holder field - generateField(BSHSTATIC+className, "Lbsh/This;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, cw); - // Generate class UUID - generateField("UUID", "Ljava/lang/String;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, this.uuid, cw); - - // Generate the fields - for (Variable var : vars) { - // Don't generate private fields - if (var.hasModifier("private")) - continue; - - String fType = var.getTypeDescriptor(); - int modifiers = getASMModifiers(var.getModifiers()); - - if ( type == INTERFACE ) { - var.setConstant(); - classStaticNameSpace.setVariableImpl(var); - // keep constant fields virtual - continue; - } else if ( type == ENUM && var.hasModifier("enum") ) { - modifiers |= ACC_ENUM | ACC_FINAL; - fType = classDescript; - } - - generateField(var.getName(), fType, modifiers, cw); - } - - if (type == ENUM) - generateEnumSupport(fqClassName, className, classDescript, cw); - - // Generate the static initializer. - generateStaticInitializer(cw); - - // Generate the constructors - boolean hasConstructor = false; - for (int i = 0; i < constructors.length; i++) { - // Don't generate private constructors - if (constructors[i].hasModifier("private")) - continue; - - int modifiers = getASMModifiers(constructors[i].getModifiers()); - if (constructors[i].isVarArgs()) - modifiers |= ACC_VARARGS; - generateConstructor(i, constructors[i].getParamTypeDescriptors(), modifiers, cw); - hasConstructor = true; - } - - // If no other constructors, generate a default constructor - if ( type == CLASS && !hasConstructor ) - generateConstructor(DEFAULTCONSTRUCTOR/*index*/, new String[0], ACC_PUBLIC, cw); - - // Generate methods - for (DelayedEvalBshMethod method : methods) { - - // Don't generate private methods - if (method.hasModifier("private")) - continue; - - if ( type == INTERFACE - && !method.hasModifier("static") - && !method.hasModifier("default") - && !method.hasModifier("abstract") ) - method.getModifiers().addModifier("abstract"); - int modifiers = getASMModifiers(method.getModifiers()); - if (method.isVarArgs()) - modifiers |= ACC_VARARGS; - boolean isStatic = (modifiers & ACC_STATIC) > 0; - - generateMethod(className, fqClassName, method.getName(), method.getReturnTypeDescriptor(), - method.getParamTypeDescriptors(), modifiers, cw); - - // check if method overrides existing method and generate super delegate. - if ( null != classContainsMethod(superClass, method.getName(), method.getParamTypeDescriptors()) && !isStatic ) - generateSuperDelegateMethod(superClass, superClassName, method.getName(), method.getReturnTypeDescriptor(), - method.getParamTypeDescriptors(), ACC_PUBLIC, cw); - } - - return cw.toByteArray(); - } - - /** - * Translate bsh.Modifiers into ASM modifier bitflags. - * Only a subset of modifiers are baked into classes. - */ - private static int getASMModifiers(Modifiers modifiers) { - int mods = 0; - - if (modifiers.hasModifier(ACC_PUBLIC)) - mods |= ACC_PUBLIC; - if (modifiers.hasModifier(ACC_PRIVATE)) - mods |= ACC_PRIVATE; - if (modifiers.hasModifier(ACC_PROTECTED)) - mods |= ACC_PROTECTED; - if (modifiers.hasModifier(ACC_STATIC)) - mods |= ACC_STATIC; - if (modifiers.hasModifier(ACC_SYNCHRONIZED)) - mods |= ACC_SYNCHRONIZED; - if (modifiers.hasModifier(ACC_ABSTRACT)) - mods |= ACC_ABSTRACT; - - // if no access modifiers declared then we make it public - if ( ( modifiers.getModifiers() & ACCESS_MODIFIERS ) == 0 ) { - mods |= ACC_PUBLIC; - modifiers.addModifier(ACC_PUBLIC); - } - - return mods; - } - - /** Generate a field - static or instance. */ - private static void generateField(String fieldName, String type, int modifiers, ClassWriter cw) { - generateField(fieldName, type, modifiers, null/*value*/, cw); - } - /** Generate field and assign initial value. */ - private static void generateField(String fieldName, String type, int modifiers, Object value, ClassWriter cw) { - cw.visitField(modifiers, fieldName, type, null/*signature*/, value); - } - - /** - * Build the signature for the supplied parameter types. - * @param paramTypes list of parameter types - * @return parameter type signature - */ - private static String getTypeParameterSignature(String[] paramTypes) { - StringBuilder sb = new StringBuilder("<"); - for (final String pt : paramTypes) - sb.append(pt).append(":"); - return sb.toString(); - } - - /** Generate support code needed for Enum types. - * Generates enum values and valueOf methods, default private constructor with initInstance call. - * Instead of maintaining a synthetic array of enum values we greatly reduce the required bytecode - * needed by delegating to This.enumValues and building the array dynamically. - * @param fqClassName fully qualified class name - * @param className class name string - * @param classDescript class descriptor string - * @param cw current class writer */ - private void generateEnumSupport(String fqClassName, String className, String classDescript, ClassWriter cw) { - // generate enum values() method delegated to static This.enumValues. - MethodVisitor cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "values", "()["+classDescript, null, null); - pushBshStatic(fqClassName, className, cv); - cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "enumValues", "()[Ljava/lang/Object;", false); - generatePlainReturnCode("["+classDescript, cv); - cv.visitMaxs(0, 0); - // generate Enum.valueOf delegate method - cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)"+classDescript, null, null); - cv.visitLdcInsn(Type.getType(classDescript)); - cv.visitVarInsn(ALOAD, 0); - cv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false); - generatePlainReturnCode(classDescript, cv); - cv.visitMaxs(0, 0); - // generate default private constructor and initInstance call - cv = cw.visitMethod(ACC_PRIVATE, "", "(Ljava/lang/String;I)V", null, null); - cv.visitVarInsn(ALOAD, 0); - cv.visitVarInsn(ALOAD, 1); - cv.visitVarInsn(ILOAD, 2); - cv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "", "(Ljava/lang/String;I)V", false); - cv.visitVarInsn(ALOAD, 0); - cv.visitLdcInsn(className); - generateParameterReifierCode(new String[0], false/*isStatic*/, cv); - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); - cv.visitInsn(RETURN); - cv.visitMaxs(0, 0); - } - - /** Generate the static initialization of the enum constants. Called from clinit. - * @param fqClassName fully qualified class name - * @param classDescript class descriptor string - * @param cv clinit method visitor */ - private void generateEnumStaticInit(String fqClassName, String classDescript, MethodVisitor cv) { - int ordinal = ICONST_0; - for ( Variable var : vars ) if ( var.hasModifier("enum") ) { - cv.visitTypeInsn(NEW, fqClassName); - cv.visitInsn(DUP); - cv.visitLdcInsn(var.getName()); - if ( ICONST_5 >= ordinal ) - cv.visitInsn(ordinal++); - else - cv.visitIntInsn(BIPUSH, ordinal++ - ICONST_0); - cv.visitMethodInsn(INVOKESPECIAL, fqClassName, "", "(Ljava/lang/String;I)V", false); - cv.visitFieldInsn(PUTSTATIC, fqClassName, var.getName(), classDescript); - } - } - - /** - * Generate a delegate method - static or instance. - * The generated code packs the method arguments into an object array - * (wrapping primitive types in bsh.Primitive), invokes the static or - * instance This invokeMethod() method, and then returns - * the result. - */ - private void generateMethod(String className, String fqClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { - String[] exceptions = null; - boolean isStatic = (modifiers & ACC_STATIC) != 0; - - if (returnType == null) // map loose return type to Object - returnType = OBJECT; - - String methodDescriptor = getMethodDescriptor(returnType, paramTypes); - - String paramTypesSig = getTypeParameterSignature(paramTypes); - - // Generate method body - MethodVisitor cv = cw.visitMethod(modifiers, methodName, methodDescriptor, paramTypesSig, exceptions); - - if ((modifiers & ACC_ABSTRACT) != 0) - return; - - // Generate code to push the BSHTHIS or BSHSTATIC field - if ( isStatic||type == INTERFACE ) - pushBshStatic(fqClassName, className, cv); - else - pushBshThis(fqClassName, className, cv); - - // Push the name of the method as a constant - cv.visitLdcInsn(methodName); - - // Generate code to push arguments as an object array - generateParameterReifierCode(paramTypes, isStatic, cv); - - // Push the boolean constant 'true' (for declaredOnly) - cv.visitInsn(ICONST_1); - - // Invoke the method This.invokeMethod( name, Class [] sig, boolean ) - cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "invokeMethod", "(Ljava/lang/String;[Ljava/lang/Object;Z)Ljava/lang/Object;", false); - - // Generate code to return the value - generateReturnCode(returnType, cv); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Generate a constructor. - */ - void generateConstructor(int index, String[] paramTypes, int modifiers, ClassWriter cw) { - /** offset after params of the args object [] var */ - final int argsVar = paramTypes.length + 1; - /** offset after params of the ConstructorArgs var */ - final int consArgsVar = paramTypes.length + 2; - - String[] exceptions = null; - String methodDescriptor = getMethodDescriptor("V", paramTypes); - - String paramTypesSig = getTypeParameterSignature(paramTypes); - - // Create this constructor method - MethodVisitor cv = cw.visitMethod(modifiers, "", methodDescriptor, paramTypesSig, exceptions); - - // Generate code to push arguments as an object array - generateParameterReifierCode(paramTypes, false/*isStatic*/, cv); - cv.visitVarInsn(ASTORE, argsVar); - - // Generate the code implementing the alternate constructor switch - generateConstructorSwitch(index, argsVar, consArgsVar, cv); - - // Generate code to invoke the ClassGeneratorUtil initInstance() method - - // push 'this' - cv.visitVarInsn(ALOAD, 0); - - // Push the class/constructor name as a constant - cv.visitLdcInsn(className); - - // Push arguments as an object array - cv.visitVarInsn(ALOAD, argsVar); - - // invoke the initInstance() method - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); - - cv.visitInsn(RETURN); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Generate the static initializer for the class - */ - void generateStaticInitializer(ClassWriter cw) { - - // Generate code to invoke the ClassGeneratorUtil initStatic() method - MethodVisitor cv = cw.visitMethod(ACC_STATIC, "", "()V", null/*sig*/, null/*exceptions*/); - - // initialize _bshStaticThis - cv.visitFieldInsn(GETSTATIC, fqClassName, "UUID", "Ljava/lang/String;"); - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "pullBshStatic", "(Ljava/lang/String;)Lbsh/This;", false); - cv.visitFieldInsn(PUTSTATIC, fqClassName, BSHSTATIC+className, "Lbsh/This;"); - - if ( type == ENUM ) - generateEnumStaticInit(fqClassName, classDescript, cv); - - // equivalent of my.ClassName.class - cv.visitLdcInsn(Type.getType(classDescript)); - - // invoke the initStatic() method - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initStatic", "(Ljava/lang/Class;)V", false); - - cv.visitInsn(RETURN); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Generate a switch with a branch for each possible alternate - * constructor. This includes all superclass constructors and all - * constructors of this class. The default branch of this switch is the - * default superclass constructor. - *

- * This method also generates the code to call the static - * ClassGeneratorUtil - * getConstructorArgs() method which inspects the scripted constructor to - * find the alternate constructor signature (if any) and evaluate the - * arguments at runtime. The getConstructorArgs() method returns the - * actual arguments as well as the index of the constructor to call. - */ - void generateConstructorSwitch(int consIndex, int argsVar, int consArgsVar, - MethodVisitor cv) { - Label defaultLabel = new Label(); - Label endLabel = new Label(); - List superConstructors = BshClassManager.memberCache - .get(superClass).members(superClass.getName()); - int cases = superConstructors.size() + constructors.length; - - Label[] labels = new Label[cases]; - for (int i = 0; i < cases; i++) - labels[i] = new Label(); - - // Generate code to call ClassGeneratorUtil to get our switch index - // and give us args... - - // push super class name .class - cv.visitLdcInsn(Type.getType(BSHType.getTypeDescriptor(superClass))); - - // Push the bsh static namespace field - pushBshStatic(fqClassName, className, cv); - - // push args - cv.visitVarInsn(ALOAD, argsVar); - - // push this constructor index number onto stack - cv.visitIntInsn(BIPUSH, consIndex); - - // invoke the ClassGeneratorUtil getConstructorsArgs() method - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "getConstructorArgs", "(Ljava/lang/Class;Lbsh/This;[Ljava/lang/Object;I)" + "Lbsh/This$ConstructorArgs;", false); - - // store ConstructorArgs in consArgsVar - cv.visitVarInsn(ASTORE, consArgsVar); - - // Get the ConstructorArgs selector field from ConstructorArgs - - // push ConstructorArgs - cv.visitVarInsn(ALOAD, consArgsVar); - cv.visitFieldInsn(GETFIELD, "bsh/This$ConstructorArgs", "selector", "I"); - - // start switch - cv.visitTableSwitchInsn(0/*min*/, cases - 1/*max*/, defaultLabel, labels); - - // generate switch body - int index = 0; - for (int i = 0; i < superConstructors.size(); i++, index++) - doSwitchBranch(index, superClassName, superConstructors.get(i).getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); - for (int i = 0; i < constructors.length; i++, index++) - doSwitchBranch(index, fqClassName, constructors[i].getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); - - // generate the default branch of switch - cv.visitLabel(defaultLabel); - // default branch always invokes no args super - cv.visitVarInsn(ALOAD, 0); // push 'this' - cv.visitMethodInsn(INVOKESPECIAL, superClassName, "", "()V", false); - - // done with switch - cv.visitLabel(endLabel); - } - - // push the class static This object - private static void pushBshStatic(String fqClassName, String className, MethodVisitor cv) { - cv.visitFieldInsn(GETSTATIC, fqClassName, BSHSTATIC + className, "Lbsh/This;"); - } - - // push the class instance This object - private static void pushBshThis(String fqClassName, String className, MethodVisitor cv) { - // Push 'this' - cv.visitVarInsn(ALOAD, 0); - // Get the instance field - cv.visitFieldInsn(GETFIELD, fqClassName, BSHTHIS + className, "Lbsh/This;"); - } - - /** Generate a branch of the constructor switch. - * This method is called by generateConstructorSwitch. The code generated by this method assumes - * that the argument array is on the stack. - * @param index label index - * @param targetClassName class name - * @param paramTypes array of type descriptor strings - * @param endLabel jump label - * @param labels visit labels - * @param consArgsVar constructor args - * @param cv the code visitor to be used to generate the bytecode. */ - private void doSwitchBranch(int index, String targetClassName, String[] paramTypes, Label endLabel, - Label[] labels, int consArgsVar, MethodVisitor cv) { - cv.visitLabel(labels[index]); - - cv.visitVarInsn(ALOAD, 0); // push this before args - - // Unload the arguments from the ConstructorArgs object - for (String type : paramTypes) { - final String method; - if (type.equals("Z")) - method = "getBoolean"; - else if (type.equals("B")) - method = "getByte"; - else if (type.equals("C")) - method = "getChar"; - else if (type.equals("S")) - method = "getShort"; - else if (type.equals("I")) - method = "getInt"; - else if (type.equals("J")) - method = "getLong"; - else if (type.equals("D")) - method = "getDouble"; - else if (type.equals("F")) - method = "getFloat"; - else - method = "getObject"; - - // invoke the iterator method on the ConstructorArgs - cv.visitVarInsn(ALOAD, consArgsVar); // push the ConstructorArgs - String className = "bsh/This$ConstructorArgs"; - String retType; - if (method.equals("getObject")) - retType = OBJECT; - else - retType = type; - - cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()" + retType, false); - // if it's an object type we must do a check cast - if (method.equals("getObject")) - cv.visitTypeInsn(CHECKCAST, descriptorToClassName(type)); - } - - // invoke the constructor for this branch - String descriptor = getMethodDescriptor("V", paramTypes); - cv.visitMethodInsn(INVOKESPECIAL, targetClassName, "", descriptor, false); - cv.visitJumpInsn(GOTO, endLabel); - } - - private static String getMethodDescriptor(String returnType, String[] paramTypes) { - StringBuilder sb = new StringBuilder("("); - for (String paramType : paramTypes) - sb.append(paramType); - - sb.append(')').append(returnType); - return sb.toString(); - } - - /** - * Generate a superclass method delegate accessor method. - * These methods are specially named methods which allow access to - * overridden methods of the superclass (which the Java reflection API - * normally does not allow). - */ - // Maybe combine this with generateMethod() - private void generateSuperDelegateMethod(Class superClass, String superClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { - String[] exceptions = null; - - if (returnType == null) // map loose return to Object - returnType = OBJECT; - - String methodDescriptor = getMethodDescriptor(returnType, paramTypes); - - String paramTypesSig = getTypeParameterSignature(paramTypes); - - // Add method body - MethodVisitor cv = cw.visitMethod(modifiers, "_bshSuper" + superClass.getSimpleName() + methodName, methodDescriptor, paramTypesSig, exceptions); - - cv.visitVarInsn(ALOAD, 0); - // Push vars - int localVarIndex = 1; - for (String paramType : paramTypes) { - if (isPrimitive(paramType)) - cv.visitVarInsn(ILOAD, localVarIndex); - else - cv.visitVarInsn(ALOAD, localVarIndex); - localVarIndex += paramType.equals("D") || paramType.equals("J") ? 2 : 1; - } - - cv.visitMethodInsn(INVOKESPECIAL, superClassName, methodName, methodDescriptor, false); - - generatePlainReturnCode(returnType, cv); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** Validate abstract method implementation. - * Check that class is abstract or implements all abstract methods. - * BSH classes are not abstract which allows us to instantiate abstract - * classes. Also applies inheritance rules @see checkInheritanceRules(). - * @param type The class to check. - * @throws RuntimException if validation fails. */ - static void checkAbstractMethodImplementation(Class type) { - final List meths = new ArrayList<>(); - class Reflector { - void gatherMethods(Class type) { - if (null != type.getSuperclass()) - gatherMethods(type.getSuperclass()); - meths.addAll(Arrays.asList(type.getDeclaredMethods())); - for (Class i : type.getInterfaces()) - gatherMethods(i); - } - } - new Reflector().gatherMethods(type); - // for each filtered abstract method - meths.stream().filter( m -> ( m.getModifiers() & ACC_ABSTRACT ) > 0 ) - .forEach( method -> { - Method[] meth = meths.stream() - // find methods of the same name - .filter( m -> method.getName().equals(m.getName() ) - // not abstract nor private - && ( m.getModifiers() & (ACC_ABSTRACT|ACC_PRIVATE) ) == 0 - // with matching parameters - && Types.areSignaturesEqual( - method.getParameterTypes(), m.getParameterTypes())) - // sort most visible methods to the top - // comparator: -1 if a is public or b not public or protected - // 0 if access modifiers for a and b are equal - .sorted( (a, b) -> ( a.getModifiers() & ACC_PUBLIC ) > 0 - || ( b.getModifiers() & (ACC_PUBLIC|ACC_PROTECTED) ) == 0 - ? -1 : ( a.getModifiers() & ACCESS_MODIFIERS ) == - ( b.getModifiers() & ACCESS_MODIFIERS ) - ? 0 : 1 ) - .toArray(Method[]::new); - // with no overriding methods class must be abstract - if ( meth.length == 0 && !Reflect.getClassModifiers(type) - .hasModifier("abstract") ) - throw new RuntimeException(type.getSimpleName() - + " is not abstract and does not override abstract method " - + method.getName() + "() in " - + method.getDeclaringClass().getSimpleName()); - // apply inheritance rules to most visible method at index 0 - if ( meth.length > 0) - checkInheritanceRules(method.getModifiers(), - meth[0].getModifiers(), method.getDeclaringClass()); - }); - } - - /** Apply inheritance rules. Overridden methods may not reduce visibility. - * @param parentModifiers parent modifiers of method being overridden - * @param overriddenModifiers overridden modifiers of new method - * @param parentClass parent class name - * @return true if visibility is not reduced - * @throws RuntimeException if validation fails */ - static boolean checkInheritanceRules(int parentModifiers, int overriddenModifiers, Class parentClass) { - int prnt = parentModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); - int chld = overriddenModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); - - if ( chld == prnt || prnt == ACC_PRIVATE || chld == ACC_PUBLIC || prnt == 0 && chld != ACC_PRIVATE ) - return true; - - throw new RuntimeException("Cannot reduce the visibility of the inherited method from " - + parentClass.getName()); - } - - /** Check if method name and type descriptor signature is overridden. - * @param clas super class - * @param methodName name of method - * @param paramTypes type descriptor of parameter types - * @return matching method or null if not found */ - static Method classContainsMethod(Class clas, String methodName, String[] paramTypes) { - while ( clas != null ) { - for ( Method method : clas.getDeclaredMethods() ) - if ( method.getName().equals(methodName) - && paramTypes.length == method.getParameterCount() ) { - String[] methodParamTypes = getTypeDescriptors(method.getParameterTypes()); - boolean found = true; - for ( int j = 0; j < paramTypes.length; j++ ) - if (false == (found = paramTypes[j].equals(methodParamTypes[j]))) - break; - if (found) return method; - } - clas = clas.getSuperclass(); - } - return null; - } - - /** Generate return code for a normal bytecode - * @param returnType expect type descriptor string - * @param cv the code visitor to be used to generate the bytecode. */ - private static void generatePlainReturnCode(String returnType, MethodVisitor cv) { - if (returnType.equals("V")) - cv.visitInsn(RETURN); - else if (isPrimitive(returnType)) { - int opcode = IRETURN; - if (returnType.equals("D")) - opcode = DRETURN; - else if (returnType.equals("F")) - opcode = FRETURN; - else if (returnType.equals("J")) //long - opcode = LRETURN; - - cv.visitInsn(opcode); - } else { - cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); - cv.visitInsn(ARETURN); - } - } - - /** Generates the code to reify the arguments of the given method. - * For a method "int m (int i, String s)", this code is the bytecode - * corresponding to the "new Object[] { new bsh.Primitive(i), s }" - * expression. - * @author Eric Bruneton - * @author Pat Niemeyer - * @param cv the code visitor to be used to generate the bytecode. - * @param isStatic the enclosing methods is static */ - private void generateParameterReifierCode(String[] paramTypes, boolean isStatic, final MethodVisitor cv) { - cv.visitIntInsn(SIPUSH, paramTypes.length); - cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); - int localVarIndex = isStatic ? 0 : 1; - for (int i = 0; i < paramTypes.length; ++i) { - String param = paramTypes[i]; - cv.visitInsn(DUP); - cv.visitIntInsn(SIPUSH, i); - if (isPrimitive(param)) { - int opcode; - if (param.equals("F")) - opcode = FLOAD; - else if (param.equals("D")) - opcode = DLOAD; - else if (param.equals("J")) - opcode = LLOAD; - else - opcode = ILOAD; - - String type = "bsh/Primitive"; - cv.visitTypeInsn(NEW, type); - cv.visitInsn(DUP); - cv.visitVarInsn(opcode, localVarIndex); - cv.visitMethodInsn(INVOKESPECIAL, type, "", "(" + param + ")V", false); - cv.visitInsn(AASTORE); - } else { - // If null wrap value as bsh.Primitive.NULL. - cv.visitVarInsn(ALOAD, localVarIndex); - Label isnull = new Label(); - cv.visitJumpInsn(IFNONNULL, isnull); - cv.visitFieldInsn(GETSTATIC, "bsh/Primitive", "NULL", "Lbsh/Primitive;"); - cv.visitInsn(AASTORE); - // else store parameter as Object. - Label notnull = new Label(); - cv.visitJumpInsn(GOTO, notnull); - cv.visitLabel(isnull); - cv.visitVarInsn(ALOAD, localVarIndex); - cv.visitInsn(AASTORE); - cv.visitLabel(notnull); - } - localVarIndex += param.equals("D") || param.equals("J") ? 2 : 1; - } - } - - /** Generates the code to unreify the result of the given method. - * For a method "int m (int i, String s)", this code is the bytecode - * corresponding to the "((Integer)...).intValue()" expression. - * @author Eric Bruneton - * @author Pat Niemeyer - * @param returnType expect type descriptor string - * @param cv the code visitor to be used to generate the bytecode. */ - private void generateReturnCode(String returnType, MethodVisitor cv) { - if (returnType.equals("V")) { - cv.visitInsn(POP); - cv.visitInsn(RETURN); - } else if (isPrimitive(returnType)) { - int opcode = IRETURN; - String type; - String meth; - if (returnType.equals("Z")) { - type = "java/lang/Boolean"; - meth = "booleanValue"; - } else if (returnType.equals("C")) { - type = "java/lang/Character"; - meth = "charValue"; - } else if (returnType.equals("B")) { - type = "java/lang/Byte"; - meth = "byteValue"; - } else if (returnType.equals("S") ) { - type = "java/lang/Short"; - meth = "shortValue"; - } else if (returnType.equals("F")) { - opcode = FRETURN; - type = "java/lang/Float"; - meth = "floatValue"; - } else if (returnType.equals("J")) { - opcode = LRETURN; - type = "java/lang/Long"; - meth = "longValue"; - } else if (returnType.equals("D")) { - opcode = DRETURN; - type = "java/lang/Double"; - meth = "doubleValue"; - } else /*if (returnType.equals("I"))*/ { - type = "java/lang/Integer"; - meth = "intValue"; - } - - String desc = returnType; - cv.visitTypeInsn(CHECKCAST, type); // type is correct here - cv.visitMethodInsn(INVOKEVIRTUAL, type, meth, "()" + desc, false); - cv.visitInsn(opcode); - } else { - cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); - cv.visitInsn(ARETURN); - } - } - - /** - * Does the type descriptor string describe a primitive type? - */ - private static boolean isPrimitive(String typeDescriptor) { - return typeDescriptor.length() == 1; // right? - } - - /** Returns type descriptors for the parameter types. - * @param cparams class list of parameter types - * @return String list of type descriptors */ - static String[] getTypeDescriptors(Class[] cparams) { - String[] sa = new String[cparams.length]; - for (int i = 0; i < sa.length; i++) - sa[i] = BSHType.getTypeDescriptor(cparams[i]); - return sa; - } - - /** If a non-array object type, remove the prefix "L" and suffix ";". - * @param s expect type descriptor string. - * @return class name */ - private static String descriptorToClassName(String s) { - if (s.startsWith("[") || !s.startsWith("L")) - return s; - return s.substring(1, s.length() - 1); - } - - /** - * Attempt to load a script named for the class: e.g. Foo.class Foo.bsh. - * The script is expected to (at minimum) initialize the class body. - * That is, it should contain the scripted class definition. - * - * This method relies on the fact that the ClassGenerator generateClass() - * method will detect that the generated class already exists and - * initialize it rather than recreating it. - * - * The only interact that this method has with the process is to initially - * cache the correct class in the class manager for the interpreter to - * insure that it is found and associated with the scripted body. - */ - public static void startInterpreterForClass(Class genClass) { - String fqClassName = genClass.getName(); - String baseName = Name.suffix(fqClassName, 1); - String resName = baseName + ".bsh"; - - URL url = genClass.getResource(resName); - if (null == url) - throw new InterpreterError("Script (" + resName + ") for BeanShell generated class: " + genClass + " not found."); - - // Set up the interpreter - try (Reader reader = new FileReader(genClass.getResourceAsStream(resName))) { - Interpreter bsh = new Interpreter(); - NameSpace globalNS = bsh.getNameSpace(); - globalNS.setName("class_" + baseName + "_global"); - globalNS.getClassManager().associateClass(genClass); +public final class ClassGeneratorUtil { + public static final int DEFAULTCONSTRUCTOR = -1; - // Source the script - bsh.eval(reader, globalNS, resName); - } catch (TargetError e) { - System.out.println("Script threw exception: " + e); - if (e.inNativeCode()) - e.printStackTrace(System.err); - } catch (IOException | EvalError e) { - System.out.println("Evaluation Error: " + e); - } + private ClassGeneratorUtil() { } } diff --git a/scripts/cn1playground/common/src/main/java/bsh/Name.java b/scripts/cn1playground/common/src/main/java/bsh/Name.java index f6c33f57f2..205d945069 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/Name.java +++ b/scripts/cn1playground/common/src/main/java/bsh/Name.java @@ -25,7 +25,6 @@ *****************************************************************************/ package bsh; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -809,14 +808,9 @@ public Object invokeMethod( if ( classNameSpace != null ) { Object instance = classNameSpace.getClassInstance(); Class classStatic = classNameSpace.classStatic; - try { - return ClassGenerator.getClassGenerator() - .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); - } catch (InvocationTargetException e) { - throw new UtilTargetError(e.getTargetException()); - } catch (ReflectError e) { - throw new UtilEvalError("Error invoking superclass method: " + e.getMessage()); - } + + return ClassGenerator.getClassGenerator() + .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); } } diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 63452b2ec9..877e15f290 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -24,6 +24,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; final class PlaygroundRunner { static final class Diagnostic { @@ -146,12 +148,50 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; + normalized = rewriteInlineAutoCloseableClasses(normalized); normalized = rewriteKnownSamCalls(normalized); normalized = rewriteLambdaArguments(normalized); String wrapped = wrapLooseScript(normalized); return wrapped == null ? normalized : wrapped; } + /** + * Minimal inline-class support for helper resources used in try-with-resources + * snippets, e.g.: + * + * class Res implements AutoCloseable { public void close() {} } + * try (Res r = new Res()) { ... } + * + * BeanShell class generation is intentionally constrained in playground runtime; + * this rewrite keeps common helper-resource patterns working without requiring + * full scripted class generation support. + */ + private String rewriteInlineAutoCloseableClasses(String script) { + Pattern declarationPattern = Pattern.compile( + "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s+implements\\s+AutoCloseable\\s*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}", + Pattern.DOTALL); + Matcher matcher = declarationPattern.matcher(script); + List helperClassNames = new ArrayList(); + while (matcher.find()) { + helperClassNames.add(matcher.group(1)); + } + if (helperClassNames.isEmpty()) { + return script; + } + + String rewritten = script; + for (int i = 0; i < helperClassNames.size(); i++) { + String className = helperClassNames.get(i); + String ctorPattern = "\\bnew\\s+" + Pattern.quote(className) + "\\s*\\(\\s*\\)"; + String replacementExpr = "(new AutoCloseable() { public void close() {} })"; + rewritten = rewritten.replaceAll(ctorPattern, replacementExpr); + } + + // Remove declarations only after constructor rewrites are done. + rewritten = declarationPattern.matcher(rewritten).replaceAll(""); + return rewritten; + } + private RunResult failure(String message, int line, int column, List inlineMessages) { int safeLine = Math.max(1, line); int safeColumn = Math.max(1, column); From 76c5eca24484477ed531b4b50e85668016fe26a8 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:00:39 +0300 Subject: [PATCH 23/38] Restore BeanShell class generation for playground runtime --- .../src/main/java/bsh/ClassGenerator.java | 293 ++++- .../src/main/java/bsh/ClassGeneratorUtil.java | 1000 ++++++++++++++++- 2 files changed, 1258 insertions(+), 35 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index d1f4a1488f..17fe6c3f87 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -15,54 +15,285 @@ * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * + * * + * * + * This file is part of the BeanShell Java Scripting distribution. * + * Documentation and updates may be found at http://www.beanshell.org/ * + * Patrick Niemeyer (pat@pat.net) * + * Author of Learning Java, O'Reilly & Associates * + * * *****************************************************************************/ - package bsh; -/** - * Scripted class generation is not supported in the CN1 playground runtime. - * This stub preserves parser/runtime references while failing explicitly if a - * script attempts to declare or synthesize classes. - */ +import static bsh.This.Keys.BSHSUPER; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + public final class ClassGenerator { - public enum Type { - CLASS, INTERFACE, ENUM - } - public static final class ClassNodeFilter implements BSHBlock.NodeFilter { - public static final ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(); - public static final ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(); + enum Type { CLASS, INTERFACE, ENUM } - private ClassNodeFilter() { + private static ClassGenerator cg; + + public static ClassGenerator getClassGenerator() { + if (cg == null) { + cg = new ClassGenerator(); } - public boolean isVisible(Node node) { - return false; + return cg; + } + + /** + * Parse the BSHBlock for the class definition and generate the class. + */ + public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { + // Delegate to the static method + return generateClassImpl(name, modifiers, interfaces, superClass, block, type, callstack, interpreter); + } + + /** + * Invoke a super.method() style superclass method on an object instance. + * This is not a normal function of the Java reflection API and is + * provided by generated class accessor methods. + */ + public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError, InvocationTargetException { + // Delegate to the static method + return invokeSuperclassMethodImpl(bcm, instance, classStatic, methodName, args); + } + + /** + * Parse the BSHBlock for for the class definition and generate the class + * using ClassGenerator. + */ + public static Class generateClassImpl(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { + NameSpace enclosingNameSpace = callstack.top(); + String packageName = enclosingNameSpace.getPackage(); + String className = enclosingNameSpace.isClass ? (enclosingNameSpace.getName() + "$" + name) : name; + String fqClassName = packageName == null ? className : packageName + "." + className; + BshClassManager bcm = interpreter.getClassManager(); + + // Create the class static namespace + NameSpace classStaticNameSpace = new NameSpace(enclosingNameSpace, className); + classStaticNameSpace.isClass = true; + + callstack.push(classStaticNameSpace); + + // Evaluate any inner class class definitions in the block + // effectively recursively call this method for contained classes first + block.evalBlock(callstack, interpreter, true/*override*/, ClassNodeFilter.CLASSCLASSES); + + // Generate the type for our class + Variable[] variables = getDeclaredVariables(block, callstack, interpreter, packageName); + DelayedEvalBshMethod[] methods = getDeclaredMethods(block, callstack, interpreter, packageName, superClass); + + callstack.pop(); + + // initialize static this singleton in namespace + classStaticNameSpace.getThis(interpreter); + + // Create the class generator, which encapsulates all knowledge of the + // structure of the class + ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(modifiers, className, packageName, superClass, interfaces, variables, methods, classStaticNameSpace, type); + + // Let the class generator install hooks relating to the structure of + // the class into the class static namespace. e.g. the constructor + // array. This is necessary whether we are generating code or just + // reinitializing a previously generated class. + classGenerator.initStaticNameSpace(classStaticNameSpace, block/*instance initializer*/); + + // Check for existing class (saved class file) + Class genClass = bcm.getAssociatedClass(fqClassName); + + // If the class isn't there then generate it. + // Else just let it be initialized below. + if (genClass == null) { + // generate bytecode, optionally with static init hooks to + // bootstrap the interpreter + byte[] code = classGenerator.generateClass(); + + if (Interpreter.getSaveClasses()) { + Interpreter.debug("Class file persistence is not available in the Codename One playground runtime."); + } + + // Define the new class in the classloader + genClass = bcm.defineClass(fqClassName, code); + Interpreter.debug("Define ", fqClassName, " as ", genClass); } + // import the unqualified class name into parent namespace + enclosingNameSpace.importClass(fqClassName.replace('$', '.')); + + // Give the static space its class static import + // important to do this after all classes are defined + classStaticNameSpace.setClassStatic(genClass); + + Interpreter.debug(classStaticNameSpace); + + if (interpreter.getStrictJava()) + ClassGeneratorUtil.checkAbstractMethodImplementation(genClass); + + return genClass; } - private static final ClassGenerator INSTANCE = new ClassGenerator(); + static Variable[] getDeclaredVariables(BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) { + List vars = new ArrayList(); + for (int child = 0; child < body.jjtGetNumChildren(); child++) { + Node node = body.jjtGetChild(child); + if (node instanceof BSHEnumConstant) { + BSHEnumConstant enm = (BSHEnumConstant) node; + try { + Variable var = new Variable(enm.getName(), + enm.getType(), null/*value*/, enm.mods); + vars.add(var); + } catch (UtilEvalError e) { + // value error shouldn't happen + } + } else if (node instanceof BSHTypedVariableDeclaration) { + BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node; + Modifiers modifiers = tvd.modifiers; + BSHVariableDeclarator[] vardec = tvd.getDeclarators(); + for (BSHVariableDeclarator aVardec : vardec) { + String name = aVardec.name; + try { + Class type = tvd.evalType(callstack, interpreter); + Variable var = new Variable(name, type, null/*value*/, modifiers); + vars.add(var); + } catch (UtilEvalError | EvalError e) { + // value error shouldn't happen + } + } + } + } - private ClassGenerator() { + return vars.toArray(new Variable[vars.size()]); } - public static ClassGenerator getClassGenerator() { - return INSTANCE; + static DelayedEvalBshMethod[] getDeclaredMethods(BSHBlock body, + CallStack callstack, Interpreter interpreter, String defaultPackage, + Class superClass) throws EvalError { + List methods = new ArrayList<>(); + if ( callstack.top().getName().indexOf("$anon") > -1 ) { + // anonymous classes need super constructor + String classBaseName = Types.getBaseName(callstack.top().getName()); + Invocable con = BshClassManager.memberCache.get(superClass) + .findMethod(superClass.getName(), + This.CONTEXT_ARGS.get().get(classBaseName)); + DelayedEvalBshMethod bm = new DelayedEvalBshMethod(classBaseName, con, callstack.top()); + methods.add(bm); + } + for (int child = 0; child < body.jjtGetNumChildren(); child++) { + Node node = body.jjtGetChild(child); + if (node instanceof BSHMethodDeclaration) { + BSHMethodDeclaration md = (BSHMethodDeclaration) node; + md.insureNodesParsed(); + Modifiers modifiers = md.modifiers; + String name = md.name; + String returnType = md.getReturnTypeDescriptor(callstack, interpreter, defaultPackage); + BSHReturnType returnTypeNode = md.getReturnTypeNode(); + BSHFormalParameters paramTypesNode = md.paramsNode; + String[] paramTypes = paramTypesNode.getTypeDescriptors(callstack, interpreter, defaultPackage); + + DelayedEvalBshMethod bm = new DelayedEvalBshMethod(name, returnType, returnTypeNode, md.paramsNode.getParamNames(), paramTypes, paramTypesNode, md.blockNode, null/*declaringNameSpace*/, modifiers, md.isVarArgs, callstack, interpreter); + + methods.add(bm); + } + } + return methods.toArray(new DelayedEvalBshMethod[methods.size()]); } - public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, - Class superClass, BSHBlock block, Type type, CallStack callstack, - Interpreter interpreter) throws EvalError { - throw new EvalError("Scripted class generation is not supported in the Codename One BeanShell runtime.", - null, callstack); + + /** + * A node filter that filters nodes for either a class body static + * initializer or instance initializer. In the static case only static + * members are passed, etc. + */ + static class ClassNodeFilter implements BSHBlock.NodeFilter { + private enum Context { STATIC, INSTANCE, CLASSES } + private enum Types { ALL, METHODS, FIELDS } + public static ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(Context.STATIC, Types.FIELDS); + public static ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(Context.STATIC, Types.METHODS); + public static ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(Context.INSTANCE, Types.FIELDS); + public static ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(Context.INSTANCE, Types.METHODS); + public static ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(Context.CLASSES); + + Context context; + Types types = Types.ALL; + + private ClassNodeFilter(Context context) { + this.context = context; + } + + private ClassNodeFilter(Context context, Types types) { + this.context = context; + this.types = types; + } + + @Override + public boolean isVisible(Node node) { + if (context == Context.CLASSES) return node instanceof BSHClassDeclaration; + + // Only show class decs in CLASSES + if (node instanceof BSHClassDeclaration) return false; + + if (context == Context.STATIC) + return types == Types.METHODS ? isStaticMethod(node) : isStatic(node); + + // context == Context.INSTANCE cannot be anything else + return types == Types.METHODS ? isInstanceMethod(node) : isNonStatic(node); + } + + private boolean isStatic(Node node) { + if ( node.jjtGetParent().jjtGetParent() instanceof BSHClassDeclaration + && ((BSHClassDeclaration) node.jjtGetParent().jjtGetParent()).type == Type.INTERFACE ) + return true; + + if (node instanceof BSHTypedVariableDeclaration) + return ((BSHTypedVariableDeclaration) node).modifiers.hasModifier("static"); + + if (node instanceof BSHBlock) + return ((BSHBlock) node).isStatic; + + return false; + } + + private boolean isNonStatic(Node node) { + if (node instanceof BSHMethodDeclaration) + return false; + return !isStatic(node); + } + + private boolean isStaticMethod(Node node) { + if (node instanceof BSHMethodDeclaration) + return ((BSHMethodDeclaration) node).modifiers.hasModifier("static"); + return false; + } + + private boolean isInstanceMethod(Node node) { + if (node instanceof BSHMethodDeclaration) + return !((BSHMethodDeclaration) node).modifiers.hasModifier("static"); + return false; + } } - public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, - Class superClass, String methodName, Object[] args) throws EvalError { - throw new EvalError("Superclass dispatch for generated classes is not supported in the Codename One BeanShell runtime.", - null, null); + /** Find and invoke the super class delegate method. */ + public static Object invokeSuperclassMethodImpl(BshClassManager bcm, + Object instance, Class classStatic, String methodName, Object[] args) + throws UtilEvalError, ReflectError, InvocationTargetException { + Class superClass = classStatic.getSuperclass(); + Class clas = instance.getClass(); + String superName = BSHSUPER + superClass.getSimpleName() + methodName; + + // look for the specially named super delegate method + Invocable superMethod = Reflect.resolveJavaMethod(clas, superName, + Types.getTypes(args), false/*onlyStatic*/); + if (superMethod != null) return superMethod.invoke(instance, args); + + // No super method, try to invoke regular method + // could be a superfluous "super." which is legal. + superMethod = Reflect.resolveExpectedJavaMethod(bcm, superClass, instance, + methodName, args, false/*onlyStatic*/); + return superMethod.invoke(instance, args); } + } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index 8483355285..ca4d8871b0 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -1,11 +1,1003 @@ +/***************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + * * + * * + * This file is part of the BeanShell Java Scripting distribution. * + * Documentation and updates may be found at http://www.beanshell.org/ * + * Patrick Niemeyer (pat@pat.net) * + * Author of Learning Java, O'Reilly & Associates * + * * + *****************************************************************************/ + package bsh; +import static bsh.ClassGenerator.Type.CLASS; +import static bsh.ClassGenerator.Type.ENUM; +import static bsh.ClassGenerator.Type.INTERFACE; +import static bsh.This.Keys.BSHCLASSMODIFIERS; +import static bsh.This.Keys.BSHCONSTRUCTORS; +import static bsh.This.Keys.BSHINIT; +import static bsh.This.Keys.BSHSTATIC; +import static bsh.This.Keys.BSHTHIS; + +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import bsh.org.objectweb.asm.ClassWriter; +import bsh.org.objectweb.asm.Label; +import bsh.org.objectweb.asm.MethodVisitor; +import bsh.org.objectweb.asm.Opcodes; +import bsh.org.objectweb.asm.Type; + /** - * Placeholder for the disabled class-generation pipeline. + * ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator + * by Eric Bruneton in order to generate class "stubs" for BeanShell at + * runtime. + *

+ *

+ * Stub classes contain all of the fields of a BeanShell scripted class + * as well as two "callback" references to BeanShell namespaces: one for + * static methods and one for instance methods. Methods of the class are + * delegators which invoke corresponding methods on either the static or + * instance bsh object and then unpack and return the results. The static + * namespace utilizes a static import to delegate variable access to the + * class' static fields. The instance namespace utilizes a dynamic import + * (i.e. mixin) to delegate variable access to the class' instance variables. + *

+ *

+ * Constructors for the class delegate to the static initInstance() method of + * ClassGeneratorUtil to initialize new instances of the object. initInstance() + * invokes the instance intializer code (init vars and instance blocks) and + * then delegates to the corresponding scripted constructor method in the + * instance namespace. Constructors contain special switch logic which allows + * the BeanShell to control the calling of alternate constructors (this() or + * super() references) at runtime. + *

+ *

+ * Specially named superclass delegator methods are also generated in order to + * allow BeanShell to access overridden methods of the superclass (which + * reflection does not normally allow). + *

+ * + * @author Pat Niemeyer */ -public final class ClassGeneratorUtil { - public static final int DEFAULTCONSTRUCTOR = -1; +public class ClassGeneratorUtil implements Opcodes { + /** + * The switch branch number for the default constructor. + * The value -1 will cause the default branch to be taken. + */ + static final int DEFAULTCONSTRUCTOR = -1; + static final int ACCESS_MODIFIERS = + ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; + + private static final String OBJECT = "Ljava/lang/Object;"; + + private final String className; + private final String classDescript; + /** + * fully qualified class name (with package) e.g. foo/bar/Blah + */ + private final String fqClassName; + private final String uuid; + private final Class superClass; + private final String superClassName; + private final Class[] interfaces; + private final Variable[] vars; + private final DelayedEvalBshMethod[] constructors; + private final DelayedEvalBshMethod[] methods; + private final Modifiers classModifiers; + private final ClassGenerator.Type type; + + /** + * @param packageName e.g. "com.foo.bar" + */ + public ClassGeneratorUtil(Modifiers classModifiers, String className, + String packageName, Class superClass, Class[] interfaces, + Variable[] vars, DelayedEvalBshMethod[] bshmethods, + NameSpace classStaticNameSpace, ClassGenerator.Type type) { + this.classModifiers = classModifiers; + this.className = className; + this.type = type; + if (packageName != null) + this.fqClassName = packageName.replace('.', '/') + "/" + className; + else + this.fqClassName = className; + this.classDescript = "L"+fqClassName.replace('.', '/')+";"; + + if (superClass == null) + if (type == ENUM) + superClass = Enum.class; + else + superClass = Object.class; + this.superClass = superClass; + this.superClassName = Type.getInternalName(superClass); + if (interfaces == null) + interfaces = Reflect.ZERO_TYPES; + this.interfaces = interfaces; + this.vars = vars; + classStaticNameSpace.isInterface = type == INTERFACE; + classStaticNameSpace.isEnum = type == ENUM; + This.contextStore.put(this.uuid = UUID.randomUUID().toString(), classStaticNameSpace); + + // Split the methods into constructors and regular method lists + List consl = new ArrayList<>(); + List methodsl = new ArrayList<>(); + String classBaseName = Types.getBaseName(className); // for inner classes + for (DelayedEvalBshMethod bshmethod : bshmethods) + if (bshmethod.getName().equals(classBaseName)) { + if (!bshmethod.modifiers.isAppliedContext(Modifiers.CONSTRUCTOR)) + bshmethod.modifiers.changeContext(Modifiers.CONSTRUCTOR); + consl.add(bshmethod); + } else + methodsl.add(bshmethod); + + constructors = consl.toArray(new DelayedEvalBshMethod[consl.size()]); + methods = methodsl.toArray(new DelayedEvalBshMethod[methodsl.size()]); + + Interpreter.debug("Generate class ", type, " ", fqClassName, " cons:", + consl.size(), " meths:", methodsl.size(), " vars:", vars.length); + + if (type == INTERFACE && !classModifiers.hasModifier("abstract")) + classModifiers.addModifier("abstract"); + if (type == ENUM && !classModifiers.hasModifier("static")) + classModifiers.addModifier("static"); + } + + /** + * This method provides a hook for the class generator implementation to + * store additional information in the class's bsh static namespace. + * Currently this is used to store an array of consructors corresponding + * to the constructor switch in the generated class. + * + * This method must be called to initialize the static space even if we + * are using a previously generated class. + */ + public void initStaticNameSpace(NameSpace classStaticNameSpace, BSHBlock instanceInitBlock) { + try { + classStaticNameSpace.setLocalVariable(""+BSHCLASSMODIFIERS, classModifiers, false/*strict*/); + classStaticNameSpace.setLocalVariable(""+BSHCONSTRUCTORS, constructors, false/*strict*/); + classStaticNameSpace.setLocalVariable(""+BSHINIT, instanceInitBlock, false/*strict*/); + } catch (UtilEvalError e) { + throw new InterpreterError("Unable to init class static block: " + e, e); + } + } + + /** + * Generate the class bytecode for this class. + */ + public byte[] generateClass() { + NameSpace classStaticNameSpace = This.contextStore.get(this.uuid); + // Force the class public for now... + int classMods = getASMModifiers(classModifiers) | ACC_PUBLIC; + if (type == INTERFACE) + classMods |= ACC_INTERFACE | ACC_ABSTRACT; + else if (type == ENUM) + classMods |= ACC_FINAL | ACC_SUPER | ACC_ENUM; + else { + classMods |= ACC_SUPER; + if ( (classMods & ACC_ABSTRACT) > 0 ) + // bsh classes are not abstract + classMods -= ACC_ABSTRACT; + } + + String[] interfaceNames = new String[interfaces.length + 1]; // +1 for GeneratedClass + for (int i = 0; i < interfaces.length; i++) { + interfaceNames[i] = Type.getInternalName(interfaces[i]); + if (Reflect.isGeneratedClass(interfaces[i])) + for (Variable v : Reflect.getVariables(interfaces[i])) + classStaticNameSpace.setVariableImpl(v); + } + // Everyone implements GeneratedClass + interfaceNames[interfaces.length] = Type.getInternalName(GeneratedClass.class); + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + String signature = type == ENUM ? "Ljava/lang/Enum<"+classDescript+">;" : null; + cw.visit(V1_8, classMods, fqClassName, signature, superClassName, interfaceNames); + + if ( type != INTERFACE ) + // Generate the bsh instance 'This' reference holder field + generateField(BSHTHIS+className, "Lbsh/This;", ACC_PUBLIC, cw); + // Generate the static bsh static This reference holder field + generateField(BSHSTATIC+className, "Lbsh/This;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, cw); + // Generate class UUID + generateField("UUID", "Ljava/lang/String;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, this.uuid, cw); + + // Generate the fields + for (Variable var : vars) { + // Don't generate private fields + if (var.hasModifier("private")) + continue; + + String fType = var.getTypeDescriptor(); + int modifiers = getASMModifiers(var.getModifiers()); + + if ( type == INTERFACE ) { + var.setConstant(); + classStaticNameSpace.setVariableImpl(var); + // keep constant fields virtual + continue; + } else if ( type == ENUM && var.hasModifier("enum") ) { + modifiers |= ACC_ENUM | ACC_FINAL; + fType = classDescript; + } + + generateField(var.getName(), fType, modifiers, cw); + } + + if (type == ENUM) + generateEnumSupport(fqClassName, className, classDescript, cw); + + // Generate the static initializer. + generateStaticInitializer(cw); + + // Generate the constructors + boolean hasConstructor = false; + for (int i = 0; i < constructors.length; i++) { + // Don't generate private constructors + if (constructors[i].hasModifier("private")) + continue; + + int modifiers = getASMModifiers(constructors[i].getModifiers()); + if (constructors[i].isVarArgs()) + modifiers |= ACC_VARARGS; + generateConstructor(i, constructors[i].getParamTypeDescriptors(), modifiers, cw); + hasConstructor = true; + } + + // If no other constructors, generate a default constructor + if ( type == CLASS && !hasConstructor ) + generateConstructor(DEFAULTCONSTRUCTOR/*index*/, new String[0], ACC_PUBLIC, cw); + + // Generate methods + for (DelayedEvalBshMethod method : methods) { + + // Don't generate private methods + if (method.hasModifier("private")) + continue; + + if ( type == INTERFACE + && !method.hasModifier("static") + && !method.hasModifier("default") + && !method.hasModifier("abstract") ) + method.getModifiers().addModifier("abstract"); + int modifiers = getASMModifiers(method.getModifiers()); + if (method.isVarArgs()) + modifiers |= ACC_VARARGS; + boolean isStatic = (modifiers & ACC_STATIC) > 0; + + generateMethod(className, fqClassName, method.getName(), method.getReturnTypeDescriptor(), + method.getParamTypeDescriptors(), modifiers, cw); + + // check if method overrides existing method and generate super delegate. + if ( null != classContainsMethod(superClass, method.getName(), method.getParamTypeDescriptors()) && !isStatic ) + generateSuperDelegateMethod(superClass, superClassName, method.getName(), method.getReturnTypeDescriptor(), + method.getParamTypeDescriptors(), ACC_PUBLIC, cw); + } + + return cw.toByteArray(); + } + + /** + * Translate bsh.Modifiers into ASM modifier bitflags. + * Only a subset of modifiers are baked into classes. + */ + private static int getASMModifiers(Modifiers modifiers) { + int mods = 0; + + if (modifiers.hasModifier(ACC_PUBLIC)) + mods |= ACC_PUBLIC; + if (modifiers.hasModifier(ACC_PRIVATE)) + mods |= ACC_PRIVATE; + if (modifiers.hasModifier(ACC_PROTECTED)) + mods |= ACC_PROTECTED; + if (modifiers.hasModifier(ACC_STATIC)) + mods |= ACC_STATIC; + if (modifiers.hasModifier(ACC_SYNCHRONIZED)) + mods |= ACC_SYNCHRONIZED; + if (modifiers.hasModifier(ACC_ABSTRACT)) + mods |= ACC_ABSTRACT; + + // if no access modifiers declared then we make it public + if ( ( modifiers.getModifiers() & ACCESS_MODIFIERS ) == 0 ) { + mods |= ACC_PUBLIC; + modifiers.addModifier(ACC_PUBLIC); + } + + return mods; + } + + /** Generate a field - static or instance. */ + private static void generateField(String fieldName, String type, int modifiers, ClassWriter cw) { + generateField(fieldName, type, modifiers, null/*value*/, cw); + } + /** Generate field and assign initial value. */ + private static void generateField(String fieldName, String type, int modifiers, Object value, ClassWriter cw) { + cw.visitField(modifiers, fieldName, type, null/*signature*/, value); + } + + /** + * Build the signature for the supplied parameter types. + * @param paramTypes list of parameter types + * @return parameter type signature + */ + private static String getTypeParameterSignature(String[] paramTypes) { + StringBuilder sb = new StringBuilder("<"); + for (final String pt : paramTypes) + sb.append(pt).append(":"); + return sb.toString(); + } + + /** Generate support code needed for Enum types. + * Generates enum values and valueOf methods, default private constructor with initInstance call. + * Instead of maintaining a synthetic array of enum values we greatly reduce the required bytecode + * needed by delegating to This.enumValues and building the array dynamically. + * @param fqClassName fully qualified class name + * @param className class name string + * @param classDescript class descriptor string + * @param cw current class writer */ + private void generateEnumSupport(String fqClassName, String className, String classDescript, ClassWriter cw) { + // generate enum values() method delegated to static This.enumValues. + MethodVisitor cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "values", "()["+classDescript, null, null); + pushBshStatic(fqClassName, className, cv); + cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "enumValues", "()[Ljava/lang/Object;", false); + generatePlainReturnCode("["+classDescript, cv); + cv.visitMaxs(0, 0); + // generate Enum.valueOf delegate method + cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)"+classDescript, null, null); + cv.visitLdcInsn(Type.getType(classDescript)); + cv.visitVarInsn(ALOAD, 0); + cv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false); + generatePlainReturnCode(classDescript, cv); + cv.visitMaxs(0, 0); + // generate default private constructor and initInstance call + cv = cw.visitMethod(ACC_PRIVATE, "", "(Ljava/lang/String;I)V", null, null); + cv.visitVarInsn(ALOAD, 0); + cv.visitVarInsn(ALOAD, 1); + cv.visitVarInsn(ILOAD, 2); + cv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "", "(Ljava/lang/String;I)V", false); + cv.visitVarInsn(ALOAD, 0); + cv.visitLdcInsn(className); + generateParameterReifierCode(new String[0], false/*isStatic*/, cv); + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); + cv.visitInsn(RETURN); + cv.visitMaxs(0, 0); + } + + /** Generate the static initialization of the enum constants. Called from clinit. + * @param fqClassName fully qualified class name + * @param classDescript class descriptor string + * @param cv clinit method visitor */ + private void generateEnumStaticInit(String fqClassName, String classDescript, MethodVisitor cv) { + int ordinal = ICONST_0; + for ( Variable var : vars ) if ( var.hasModifier("enum") ) { + cv.visitTypeInsn(NEW, fqClassName); + cv.visitInsn(DUP); + cv.visitLdcInsn(var.getName()); + if ( ICONST_5 >= ordinal ) + cv.visitInsn(ordinal++); + else + cv.visitIntInsn(BIPUSH, ordinal++ - ICONST_0); + cv.visitMethodInsn(INVOKESPECIAL, fqClassName, "", "(Ljava/lang/String;I)V", false); + cv.visitFieldInsn(PUTSTATIC, fqClassName, var.getName(), classDescript); + } + } + + /** + * Generate a delegate method - static or instance. + * The generated code packs the method arguments into an object array + * (wrapping primitive types in bsh.Primitive), invokes the static or + * instance This invokeMethod() method, and then returns + * the result. + */ + private void generateMethod(String className, String fqClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { + String[] exceptions = null; + boolean isStatic = (modifiers & ACC_STATIC) != 0; + + if (returnType == null) // map loose return type to Object + returnType = OBJECT; + + String methodDescriptor = getMethodDescriptor(returnType, paramTypes); + + String paramTypesSig = getTypeParameterSignature(paramTypes); + + // Generate method body + MethodVisitor cv = cw.visitMethod(modifiers, methodName, methodDescriptor, paramTypesSig, exceptions); + + if ((modifiers & ACC_ABSTRACT) != 0) + return; + + // Generate code to push the BSHTHIS or BSHSTATIC field + if ( isStatic||type == INTERFACE ) + pushBshStatic(fqClassName, className, cv); + else + pushBshThis(fqClassName, className, cv); + + // Push the name of the method as a constant + cv.visitLdcInsn(methodName); + + // Generate code to push arguments as an object array + generateParameterReifierCode(paramTypes, isStatic, cv); + + // Push the boolean constant 'true' (for declaredOnly) + cv.visitInsn(ICONST_1); + + // Invoke the method This.invokeMethod( name, Class [] sig, boolean ) + cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "invokeMethod", "(Ljava/lang/String;[Ljava/lang/Object;Z)Ljava/lang/Object;", false); + + // Generate code to return the value + generateReturnCode(returnType, cv); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** + * Generate a constructor. + */ + void generateConstructor(int index, String[] paramTypes, int modifiers, ClassWriter cw) { + /** offset after params of the args object [] var */ + final int argsVar = paramTypes.length + 1; + /** offset after params of the ConstructorArgs var */ + final int consArgsVar = paramTypes.length + 2; + + String[] exceptions = null; + String methodDescriptor = getMethodDescriptor("V", paramTypes); + + String paramTypesSig = getTypeParameterSignature(paramTypes); + + // Create this constructor method + MethodVisitor cv = cw.visitMethod(modifiers, "", methodDescriptor, paramTypesSig, exceptions); + + // Generate code to push arguments as an object array + generateParameterReifierCode(paramTypes, false/*isStatic*/, cv); + cv.visitVarInsn(ASTORE, argsVar); + + // Generate the code implementing the alternate constructor switch + generateConstructorSwitch(index, argsVar, consArgsVar, cv); + + // Generate code to invoke the ClassGeneratorUtil initInstance() method + + // push 'this' + cv.visitVarInsn(ALOAD, 0); + + // Push the class/constructor name as a constant + cv.visitLdcInsn(className); + + // Push arguments as an object array + cv.visitVarInsn(ALOAD, argsVar); + + // invoke the initInstance() method + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); + + cv.visitInsn(RETURN); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** + * Generate the static initializer for the class + */ + void generateStaticInitializer(ClassWriter cw) { + + // Generate code to invoke the ClassGeneratorUtil initStatic() method + MethodVisitor cv = cw.visitMethod(ACC_STATIC, "", "()V", null/*sig*/, null/*exceptions*/); + + // initialize _bshStaticThis + cv.visitFieldInsn(GETSTATIC, fqClassName, "UUID", "Ljava/lang/String;"); + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "pullBshStatic", "(Ljava/lang/String;)Lbsh/This;", false); + cv.visitFieldInsn(PUTSTATIC, fqClassName, BSHSTATIC+className, "Lbsh/This;"); + + if ( type == ENUM ) + generateEnumStaticInit(fqClassName, classDescript, cv); + + // equivalent of my.ClassName.class + cv.visitLdcInsn(Type.getType(classDescript)); + + // invoke the initStatic() method + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initStatic", "(Ljava/lang/Class;)V", false); + + cv.visitInsn(RETURN); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** + * Generate a switch with a branch for each possible alternate + * constructor. This includes all superclass constructors and all + * constructors of this class. The default branch of this switch is the + * default superclass constructor. + *

+ * This method also generates the code to call the static + * ClassGeneratorUtil + * getConstructorArgs() method which inspects the scripted constructor to + * find the alternate constructor signature (if any) and evaluate the + * arguments at runtime. The getConstructorArgs() method returns the + * actual arguments as well as the index of the constructor to call. + */ + void generateConstructorSwitch(int consIndex, int argsVar, int consArgsVar, + MethodVisitor cv) { + Label defaultLabel = new Label(); + Label endLabel = new Label(); + List superConstructors = BshClassManager.memberCache + .get(superClass).members(superClass.getName()); + int cases = superConstructors.size() + constructors.length; + + Label[] labels = new Label[cases]; + for (int i = 0; i < cases; i++) + labels[i] = new Label(); + + // Generate code to call ClassGeneratorUtil to get our switch index + // and give us args... + + // push super class name .class + cv.visitLdcInsn(Type.getType(BSHType.getTypeDescriptor(superClass))); + + // Push the bsh static namespace field + pushBshStatic(fqClassName, className, cv); + + // push args + cv.visitVarInsn(ALOAD, argsVar); + + // push this constructor index number onto stack + cv.visitIntInsn(BIPUSH, consIndex); + + // invoke the ClassGeneratorUtil getConstructorsArgs() method + cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "getConstructorArgs", "(Ljava/lang/Class;Lbsh/This;[Ljava/lang/Object;I)" + "Lbsh/This$ConstructorArgs;", false); + + // store ConstructorArgs in consArgsVar + cv.visitVarInsn(ASTORE, consArgsVar); + + // Get the ConstructorArgs selector field from ConstructorArgs + + // push ConstructorArgs + cv.visitVarInsn(ALOAD, consArgsVar); + cv.visitFieldInsn(GETFIELD, "bsh/This$ConstructorArgs", "selector", "I"); + + // start switch + cv.visitTableSwitchInsn(0/*min*/, cases - 1/*max*/, defaultLabel, labels); + + // generate switch body + int index = 0; + for (int i = 0; i < superConstructors.size(); i++, index++) + doSwitchBranch(index, superClassName, superConstructors.get(i).getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); + for (int i = 0; i < constructors.length; i++, index++) + doSwitchBranch(index, fqClassName, constructors[i].getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); + + // generate the default branch of switch + cv.visitLabel(defaultLabel); + // default branch always invokes no args super + cv.visitVarInsn(ALOAD, 0); // push 'this' + cv.visitMethodInsn(INVOKESPECIAL, superClassName, "", "()V", false); + + // done with switch + cv.visitLabel(endLabel); + } + + // push the class static This object + private static void pushBshStatic(String fqClassName, String className, MethodVisitor cv) { + cv.visitFieldInsn(GETSTATIC, fqClassName, BSHSTATIC + className, "Lbsh/This;"); + } + + // push the class instance This object + private static void pushBshThis(String fqClassName, String className, MethodVisitor cv) { + // Push 'this' + cv.visitVarInsn(ALOAD, 0); + // Get the instance field + cv.visitFieldInsn(GETFIELD, fqClassName, BSHTHIS + className, "Lbsh/This;"); + } + + /** Generate a branch of the constructor switch. + * This method is called by generateConstructorSwitch. The code generated by this method assumes + * that the argument array is on the stack. + * @param index label index + * @param targetClassName class name + * @param paramTypes array of type descriptor strings + * @param endLabel jump label + * @param labels visit labels + * @param consArgsVar constructor args + * @param cv the code visitor to be used to generate the bytecode. */ + private void doSwitchBranch(int index, String targetClassName, String[] paramTypes, Label endLabel, + Label[] labels, int consArgsVar, MethodVisitor cv) { + cv.visitLabel(labels[index]); + + cv.visitVarInsn(ALOAD, 0); // push this before args + + // Unload the arguments from the ConstructorArgs object + for (String type : paramTypes) { + final String method; + if (type.equals("Z")) + method = "getBoolean"; + else if (type.equals("B")) + method = "getByte"; + else if (type.equals("C")) + method = "getChar"; + else if (type.equals("S")) + method = "getShort"; + else if (type.equals("I")) + method = "getInt"; + else if (type.equals("J")) + method = "getLong"; + else if (type.equals("D")) + method = "getDouble"; + else if (type.equals("F")) + method = "getFloat"; + else + method = "getObject"; + + // invoke the iterator method on the ConstructorArgs + cv.visitVarInsn(ALOAD, consArgsVar); // push the ConstructorArgs + String className = "bsh/This$ConstructorArgs"; + String retType; + if (method.equals("getObject")) + retType = OBJECT; + else + retType = type; + + cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()" + retType, false); + // if it's an object type we must do a check cast + if (method.equals("getObject")) + cv.visitTypeInsn(CHECKCAST, descriptorToClassName(type)); + } + + // invoke the constructor for this branch + String descriptor = getMethodDescriptor("V", paramTypes); + cv.visitMethodInsn(INVOKESPECIAL, targetClassName, "", descriptor, false); + cv.visitJumpInsn(GOTO, endLabel); + } + + private static String getMethodDescriptor(String returnType, String[] paramTypes) { + StringBuilder sb = new StringBuilder("("); + for (String paramType : paramTypes) + sb.append(paramType); + + sb.append(')').append(returnType); + return sb.toString(); + } + + /** + * Generate a superclass method delegate accessor method. + * These methods are specially named methods which allow access to + * overridden methods of the superclass (which the Java reflection API + * normally does not allow). + */ + // Maybe combine this with generateMethod() + private void generateSuperDelegateMethod(Class superClass, String superClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { + String[] exceptions = null; + + if (returnType == null) // map loose return to Object + returnType = OBJECT; + + String methodDescriptor = getMethodDescriptor(returnType, paramTypes); + + String paramTypesSig = getTypeParameterSignature(paramTypes); + + // Add method body + MethodVisitor cv = cw.visitMethod(modifiers, "_bshSuper" + superClass.getSimpleName() + methodName, methodDescriptor, paramTypesSig, exceptions); + + cv.visitVarInsn(ALOAD, 0); + // Push vars + int localVarIndex = 1; + for (String paramType : paramTypes) { + if (isPrimitive(paramType)) + cv.visitVarInsn(ILOAD, localVarIndex); + else + cv.visitVarInsn(ALOAD, localVarIndex); + localVarIndex += paramType.equals("D") || paramType.equals("J") ? 2 : 1; + } + + cv.visitMethodInsn(INVOKESPECIAL, superClassName, methodName, methodDescriptor, false); + + generatePlainReturnCode(returnType, cv); + + // values here are ignored, computed automatically by ClassWriter + cv.visitMaxs(0, 0); + } + + /** Validate abstract method implementation. + * Check that class is abstract or implements all abstract methods. + * BSH classes are not abstract which allows us to instantiate abstract + * classes. Also applies inheritance rules @see checkInheritanceRules(). + * @param type The class to check. + * @throws RuntimException if validation fails. */ + static void checkAbstractMethodImplementation(Class type) { + final List meths = new ArrayList<>(); + class Reflector { + void gatherMethods(Class type) { + if (null != type.getSuperclass()) + gatherMethods(type.getSuperclass()); + meths.addAll(Arrays.asList(type.getDeclaredMethods())); + for (Class i : type.getInterfaces()) + gatherMethods(i); + } + } + new Reflector().gatherMethods(type); + // for each filtered abstract method + meths.stream().filter( m -> ( m.getModifiers() & ACC_ABSTRACT ) > 0 ) + .forEach( method -> { + Method[] meth = meths.stream() + // find methods of the same name + .filter( m -> method.getName().equals(m.getName() ) + // not abstract nor private + && ( m.getModifiers() & (ACC_ABSTRACT|ACC_PRIVATE) ) == 0 + // with matching parameters + && Types.areSignaturesEqual( + method.getParameterTypes(), m.getParameterTypes())) + // sort most visible methods to the top + // comparator: -1 if a is public or b not public or protected + // 0 if access modifiers for a and b are equal + .sorted( (a, b) -> ( a.getModifiers() & ACC_PUBLIC ) > 0 + || ( b.getModifiers() & (ACC_PUBLIC|ACC_PROTECTED) ) == 0 + ? -1 : ( a.getModifiers() & ACCESS_MODIFIERS ) == + ( b.getModifiers() & ACCESS_MODIFIERS ) + ? 0 : 1 ) + .toArray(Method[]::new); + // with no overriding methods class must be abstract + if ( meth.length == 0 && !Reflect.getClassModifiers(type) + .hasModifier("abstract") ) + throw new RuntimeException(type.getSimpleName() + + " is not abstract and does not override abstract method " + + method.getName() + "() in " + + method.getDeclaringClass().getSimpleName()); + // apply inheritance rules to most visible method at index 0 + if ( meth.length > 0) + checkInheritanceRules(method.getModifiers(), + meth[0].getModifiers(), method.getDeclaringClass()); + }); + } + + /** Apply inheritance rules. Overridden methods may not reduce visibility. + * @param parentModifiers parent modifiers of method being overridden + * @param overriddenModifiers overridden modifiers of new method + * @param parentClass parent class name + * @return true if visibility is not reduced + * @throws RuntimeException if validation fails */ + static boolean checkInheritanceRules(int parentModifiers, int overriddenModifiers, Class parentClass) { + int prnt = parentModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); + int chld = overriddenModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); + + if ( chld == prnt || prnt == ACC_PRIVATE || chld == ACC_PUBLIC || prnt == 0 && chld != ACC_PRIVATE ) + return true; + + throw new RuntimeException("Cannot reduce the visibility of the inherited method from " + + parentClass.getName()); + } + + /** Check if method name and type descriptor signature is overridden. + * @param clas super class + * @param methodName name of method + * @param paramTypes type descriptor of parameter types + * @return matching method or null if not found */ + static Method classContainsMethod(Class clas, String methodName, String[] paramTypes) { + while ( clas != null ) { + for ( Method method : clas.getDeclaredMethods() ) + if ( method.getName().equals(methodName) + && paramTypes.length == method.getParameterCount() ) { + String[] methodParamTypes = getTypeDescriptors(method.getParameterTypes()); + boolean found = true; + for ( int j = 0; j < paramTypes.length; j++ ) + if (false == (found = paramTypes[j].equals(methodParamTypes[j]))) + break; + if (found) return method; + } + clas = clas.getSuperclass(); + } + return null; + } + + /** Generate return code for a normal bytecode + * @param returnType expect type descriptor string + * @param cv the code visitor to be used to generate the bytecode. */ + private static void generatePlainReturnCode(String returnType, MethodVisitor cv) { + if (returnType.equals("V")) + cv.visitInsn(RETURN); + else if (isPrimitive(returnType)) { + int opcode = IRETURN; + if (returnType.equals("D")) + opcode = DRETURN; + else if (returnType.equals("F")) + opcode = FRETURN; + else if (returnType.equals("J")) //long + opcode = LRETURN; + + cv.visitInsn(opcode); + } else { + cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); + cv.visitInsn(ARETURN); + } + } + + /** Generates the code to reify the arguments of the given method. + * For a method "int m (int i, String s)", this code is the bytecode + * corresponding to the "new Object[] { new bsh.Primitive(i), s }" + * expression. + * @author Eric Bruneton + * @author Pat Niemeyer + * @param cv the code visitor to be used to generate the bytecode. + * @param isStatic the enclosing methods is static */ + private void generateParameterReifierCode(String[] paramTypes, boolean isStatic, final MethodVisitor cv) { + cv.visitIntInsn(SIPUSH, paramTypes.length); + cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + int localVarIndex = isStatic ? 0 : 1; + for (int i = 0; i < paramTypes.length; ++i) { + String param = paramTypes[i]; + cv.visitInsn(DUP); + cv.visitIntInsn(SIPUSH, i); + if (isPrimitive(param)) { + int opcode; + if (param.equals("F")) + opcode = FLOAD; + else if (param.equals("D")) + opcode = DLOAD; + else if (param.equals("J")) + opcode = LLOAD; + else + opcode = ILOAD; + + String type = "bsh/Primitive"; + cv.visitTypeInsn(NEW, type); + cv.visitInsn(DUP); + cv.visitVarInsn(opcode, localVarIndex); + cv.visitMethodInsn(INVOKESPECIAL, type, "", "(" + param + ")V", false); + cv.visitInsn(AASTORE); + } else { + // If null wrap value as bsh.Primitive.NULL. + cv.visitVarInsn(ALOAD, localVarIndex); + Label isnull = new Label(); + cv.visitJumpInsn(IFNONNULL, isnull); + cv.visitFieldInsn(GETSTATIC, "bsh/Primitive", "NULL", "Lbsh/Primitive;"); + cv.visitInsn(AASTORE); + // else store parameter as Object. + Label notnull = new Label(); + cv.visitJumpInsn(GOTO, notnull); + cv.visitLabel(isnull); + cv.visitVarInsn(ALOAD, localVarIndex); + cv.visitInsn(AASTORE); + cv.visitLabel(notnull); + } + localVarIndex += param.equals("D") || param.equals("J") ? 2 : 1; + } + } + + /** Generates the code to unreify the result of the given method. + * For a method "int m (int i, String s)", this code is the bytecode + * corresponding to the "((Integer)...).intValue()" expression. + * @author Eric Bruneton + * @author Pat Niemeyer + * @param returnType expect type descriptor string + * @param cv the code visitor to be used to generate the bytecode. */ + private void generateReturnCode(String returnType, MethodVisitor cv) { + if (returnType.equals("V")) { + cv.visitInsn(POP); + cv.visitInsn(RETURN); + } else if (isPrimitive(returnType)) { + int opcode = IRETURN; + String type; + String meth; + if (returnType.equals("Z")) { + type = "java/lang/Boolean"; + meth = "booleanValue"; + } else if (returnType.equals("C")) { + type = "java/lang/Character"; + meth = "charValue"; + } else if (returnType.equals("B")) { + type = "java/lang/Byte"; + meth = "byteValue"; + } else if (returnType.equals("S") ) { + type = "java/lang/Short"; + meth = "shortValue"; + } else if (returnType.equals("F")) { + opcode = FRETURN; + type = "java/lang/Float"; + meth = "floatValue"; + } else if (returnType.equals("J")) { + opcode = LRETURN; + type = "java/lang/Long"; + meth = "longValue"; + } else if (returnType.equals("D")) { + opcode = DRETURN; + type = "java/lang/Double"; + meth = "doubleValue"; + } else /*if (returnType.equals("I"))*/ { + type = "java/lang/Integer"; + meth = "intValue"; + } + + String desc = returnType; + cv.visitTypeInsn(CHECKCAST, type); // type is correct here + cv.visitMethodInsn(INVOKEVIRTUAL, type, meth, "()" + desc, false); + cv.visitInsn(opcode); + } else { + cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); + cv.visitInsn(ARETURN); + } + } + + /** + * Does the type descriptor string describe a primitive type? + */ + private static boolean isPrimitive(String typeDescriptor) { + return typeDescriptor.length() == 1; // right? + } + + /** Returns type descriptors for the parameter types. + * @param cparams class list of parameter types + * @return String list of type descriptors */ + static String[] getTypeDescriptors(Class[] cparams) { + String[] sa = new String[cparams.length]; + for (int i = 0; i < sa.length; i++) + sa[i] = BSHType.getTypeDescriptor(cparams[i]); + return sa; + } + + /** If a non-array object type, remove the prefix "L" and suffix ";". + * @param s expect type descriptor string. + * @return class name */ + private static String descriptorToClassName(String s) { + if (s.startsWith("[") || !s.startsWith("L")) + return s; + return s.substring(1, s.length() - 1); + } + + /** + * Attempt to load a script named for the class: e.g. Foo.class Foo.bsh. + * The script is expected to (at minimum) initialize the class body. + * That is, it should contain the scripted class definition. + * + * This method relies on the fact that the ClassGenerator generateClass() + * method will detect that the generated class already exists and + * initialize it rather than recreating it. + * + * The only interact that this method has with the process is to initially + * cache the correct class in the class manager for the interpreter to + * insure that it is found and associated with the scripted body. + */ + public static void startInterpreterForClass(Class genClass) { + String fqClassName = genClass.getName(); + String baseName = Name.suffix(fqClassName, 1); + String resName = baseName + ".bsh"; + + URL url = genClass.getResource(resName); + if (null == url) + throw new InterpreterError("Script (" + resName + ") for BeanShell generated class: " + genClass + " not found."); + + // Set up the interpreter + try (Reader reader = new FileReader(genClass.getResourceAsStream(resName))) { + Interpreter bsh = new Interpreter(); + NameSpace globalNS = bsh.getNameSpace(); + globalNS.setName("class_" + baseName + "_global"); + globalNS.getClassManager().associateClass(genClass); - private ClassGeneratorUtil() { + // Source the script + bsh.eval(reader, globalNS, resName); + } catch (TargetError e) { + System.out.println("Script threw exception: " + e); + if (e.inNativeCode()) + e.printStackTrace(System.err); + } catch (IOException | EvalError e) { + System.out.println("Evaluation Error: " + e); + } } } From ea8c2218082a4932f4e93c2ab2017cdfdc078003 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:22:52 +0300 Subject: [PATCH 24/38] Handle superclass invocation target exceptions in Name --- .../cn1playground/common/src/main/java/bsh/Name.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/Name.java b/scripts/cn1playground/common/src/main/java/bsh/Name.java index 205d945069..24ad2f074a 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/Name.java +++ b/scripts/cn1playground/common/src/main/java/bsh/Name.java @@ -25,10 +25,11 @@ *****************************************************************************/ package bsh; +import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.HashMap; /** What's in a name? I'll tell you... @@ -809,8 +810,13 @@ public Object invokeMethod( Object instance = classNameSpace.getClassInstance(); Class classStatic = classNameSpace.classStatic; - return ClassGenerator.getClassGenerator() - .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); + try { + return ClassGenerator.getClassGenerator() + .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); + } catch (InvocationTargetException e) { + throw new UtilTargetError( + "Error invoking superclass method: " + methodName, e); + } } } From 5f9bbfa054aabe16c66ed5fbc81ad5e73ac5fc04 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:22:57 +0300 Subject: [PATCH 25/38] Remove InvocationTargetException from CN1 playground path --- .../common/src/main/java/bsh/ClassGenerator.java | 5 ++--- .../cn1playground/common/src/main/java/bsh/Name.java | 10 ++-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index 17fe6c3f87..c7f09e3ef4 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -27,7 +27,6 @@ import static bsh.This.Keys.BSHSUPER; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; @@ -58,7 +57,7 @@ public Class generateClass(String name, Modifiers modifiers, Class[] inter * This is not a normal function of the Java reflection API and is * provided by generated class accessor methods. */ - public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError, InvocationTargetException { + public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError { // Delegate to the static method return invokeSuperclassMethodImpl(bcm, instance, classStatic, methodName, args); } @@ -279,7 +278,7 @@ private boolean isInstanceMethod(Node node) { /** Find and invoke the super class delegate method. */ public static Object invokeSuperclassMethodImpl(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) - throws UtilEvalError, ReflectError, InvocationTargetException { + throws UtilEvalError, ReflectError { Class superClass = classStatic.getSuperclass(); Class clas = instance.getClass(); String superName = BSHSUPER + superClass.getSimpleName() + methodName; diff --git a/scripts/cn1playground/common/src/main/java/bsh/Name.java b/scripts/cn1playground/common/src/main/java/bsh/Name.java index 24ad2f074a..4121283bbe 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/Name.java +++ b/scripts/cn1playground/common/src/main/java/bsh/Name.java @@ -25,7 +25,6 @@ *****************************************************************************/ package bsh; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -810,13 +809,8 @@ public Object invokeMethod( Object instance = classNameSpace.getClassInstance(); Class classStatic = classNameSpace.classStatic; - try { - return ClassGenerator.getClassGenerator() - .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); - } catch (InvocationTargetException e) { - throw new UtilTargetError( - "Error invoking superclass method: " + methodName, e); - } + return ClassGenerator.getClassGenerator() + .invokeSuperclassMethod( bcm, instance, classStatic, methodName, args ); } } From 1f5abcb960cd06c6a28510fee085d45ff6cb11e2 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:59:54 +0300 Subject: [PATCH 26/38] Remove unsupported reflection and regex usage from playground runtime --- .../src/main/java/bsh/ClassGenerator.java | 13 +++--- .../src/main/java/bsh/ClassGeneratorUtil.java | 32 ++++++++------- .../playground/PlaygroundRunner.java | 40 ------------------- 3 files changed, 23 insertions(+), 62 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index c7f09e3ef4..c46a56ff2d 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -279,20 +279,17 @@ private boolean isInstanceMethod(Node node) { public static Object invokeSuperclassMethodImpl(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError { - Class superClass = classStatic.getSuperclass(); Class clas = instance.getClass(); - String superName = BSHSUPER + superClass.getSimpleName() + methodName; + String superName = BSHSUPER + methodName; // look for the specially named super delegate method Invocable superMethod = Reflect.resolveJavaMethod(clas, superName, Types.getTypes(args), false/*onlyStatic*/); - if (superMethod != null) return superMethod.invoke(instance, args); + if (superMethod != null) { + return superMethod.invoke(instance, args); + } - // No super method, try to invoke regular method - // could be a superfluous "super." which is legal. - superMethod = Reflect.resolveExpectedJavaMethod(bcm, superClass, instance, - methodName, args, false/*onlyStatic*/); - return superMethod.invoke(instance, args); + throw new ReflectError("Superclass method delegate not found: " + methodName); } } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index ca4d8871b0..9262e7f222 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -33,6 +33,7 @@ import static bsh.This.Keys.BSHCONSTRUCTORS; import static bsh.This.Keys.BSHINIT; import static bsh.This.Keys.BSHSTATIC; +import static bsh.This.Keys.BSHSUPER; import static bsh.This.Keys.BSHTHIS; import java.io.IOException; @@ -695,7 +696,7 @@ private void generateSuperDelegateMethod(Class superClass, String superClassN String paramTypesSig = getTypeParameterSignature(paramTypes); // Add method body - MethodVisitor cv = cw.visitMethod(modifiers, "_bshSuper" + superClass.getSimpleName() + methodName, methodDescriptor, paramTypesSig, exceptions); + MethodVisitor cv = cw.visitMethod(modifiers, BSHSUPER + methodName, methodDescriptor, paramTypesSig, exceptions); cv.visitVarInsn(ALOAD, 0); // Push vars @@ -726,8 +727,6 @@ static void checkAbstractMethodImplementation(Class type) { final List meths = new ArrayList<>(); class Reflector { void gatherMethods(Class type) { - if (null != type.getSuperclass()) - gatherMethods(type.getSuperclass()); meths.addAll(Arrays.asList(type.getDeclaredMethods())); for (Class i : type.getInterfaces()) gatherMethods(i); @@ -791,18 +790,23 @@ static boolean checkInheritanceRules(int parentModifiers, int overriddenModifier * @param paramTypes type descriptor of parameter types * @return matching method or null if not found */ static Method classContainsMethod(Class clas, String methodName, String[] paramTypes) { - while ( clas != null ) { - for ( Method method : clas.getDeclaredMethods() ) - if ( method.getName().equals(methodName) - && paramTypes.length == method.getParameterCount() ) { - String[] methodParamTypes = getTypeDescriptors(method.getParameterTypes()); - boolean found = true; - for ( int j = 0; j < paramTypes.length; j++ ) - if (false == (found = paramTypes[j].equals(methodParamTypes[j]))) - break; - if (found) return method; + if (clas == null) { + return null; + } + for (Method method : clas.getDeclaredMethods()) { + if (method.getName().equals(methodName) + && paramTypes.length == method.getParameterCount()) { + String[] methodParamTypes = getTypeDescriptors(method.getParameterTypes()); + boolean found = true; + for (int j = 0; j < paramTypes.length; j++) { + if (false == (found = paramTypes[j].equals(methodParamTypes[j]))) { + break; + } + } + if (found) { + return method; } - clas = clas.getSuperclass(); + } } return null; } diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 877e15f290..63452b2ec9 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -24,8 +24,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; final class PlaygroundRunner { static final class Diagnostic { @@ -148,50 +146,12 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; - normalized = rewriteInlineAutoCloseableClasses(normalized); normalized = rewriteKnownSamCalls(normalized); normalized = rewriteLambdaArguments(normalized); String wrapped = wrapLooseScript(normalized); return wrapped == null ? normalized : wrapped; } - /** - * Minimal inline-class support for helper resources used in try-with-resources - * snippets, e.g.: - * - * class Res implements AutoCloseable { public void close() {} } - * try (Res r = new Res()) { ... } - * - * BeanShell class generation is intentionally constrained in playground runtime; - * this rewrite keeps common helper-resource patterns working without requiring - * full scripted class generation support. - */ - private String rewriteInlineAutoCloseableClasses(String script) { - Pattern declarationPattern = Pattern.compile( - "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s+implements\\s+AutoCloseable\\s*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}", - Pattern.DOTALL); - Matcher matcher = declarationPattern.matcher(script); - List helperClassNames = new ArrayList(); - while (matcher.find()) { - helperClassNames.add(matcher.group(1)); - } - if (helperClassNames.isEmpty()) { - return script; - } - - String rewritten = script; - for (int i = 0; i < helperClassNames.size(); i++) { - String className = helperClassNames.get(i); - String ctorPattern = "\\bnew\\s+" + Pattern.quote(className) + "\\s*\\(\\s*\\)"; - String replacementExpr = "(new AutoCloseable() { public void close() {} })"; - rewritten = rewritten.replaceAll(ctorPattern, replacementExpr); - } - - // Remove declarations only after constructor rewrites are done. - rewritten = declarationPattern.matcher(rewritten).replaceAll(""); - return rewritten; - } - private RunResult failure(String message, int line, int column, List inlineMessages) { int safeLine = Math.max(1, line); int safeColumn = Math.max(1, column); From dae01500e12eea9ac2f495c739315e54abdd063c Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:00:00 +0300 Subject: [PATCH 27/38] Use CN1 RE/StringUtil for inline AutoCloseable rewrite --- .../playground/PlaygroundRunner.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 63452b2ec9..681dffd134 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -20,6 +20,8 @@ import com.codename1.ui.layouts.LayeredLayout; import com.codename1.ui.plaf.Style; import com.codename1.ui.plaf.UIManager; +import com.codename1.util.StringUtil; +import com.codename1.util.regex.RE; import java.util.ArrayList; import java.util.Collections; @@ -146,12 +148,58 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; + normalized = rewriteInlineAutoCloseableClasses(normalized); normalized = rewriteKnownSamCalls(normalized); normalized = rewriteLambdaArguments(normalized); String wrapped = wrapLooseScript(normalized); return wrapped == null ? normalized : wrapped; } + private String rewriteInlineAutoCloseableClasses(String script) { + RE declarationPattern = new RE( + "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s+implements\\s+AutoCloseable\\s*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}"); + List helperClassNames = new ArrayList(); + int searchFrom = 0; + while (searchFrom < script.length() && declarationPattern.match(script, searchFrom)) { + helperClassNames.add(declarationPattern.getParen(1)); + int next = declarationPattern.getParenEnd(0); + if (next <= searchFrom) { + break; + } + searchFrom = next; + } + if (helperClassNames.isEmpty()) { + return script; + } + + String rewritten = script; + for (int i = 0; i < helperClassNames.size(); i++) { + String className = helperClassNames.get(i); + String classToken = escapeRegexLiteral(className); + RE ctorPattern = new RE("\\bnew\\s+" + classToken + "\\s*\\(\\s*\\)"); + rewritten = ctorPattern.subst(rewritten, + "(new AutoCloseable() { public void close() {} })", RE.REPLACE_ALL); + rewritten = StringUtil.replaceAll(rewritten, "new " + className + "()", + "(new AutoCloseable() { public void close() {} })"); + } + rewritten = declarationPattern.subst(rewritten, "", RE.REPLACE_ALL); + return rewritten; + } + + private String escapeRegexLiteral(String value) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + if (ch == '\\' || ch == '$' || ch == '.' || ch == '[' || ch == ']' || ch == '(' || ch == ')' + || ch == '{' || ch == '}' || ch == '+' || ch == '*' || ch == '?' || ch == '^' + || ch == '|') { + out.append('\\'); + } + out.append(ch); + } + return out.toString(); + } + private RunResult failure(String message, int line, int column, List inlineMessages) { int safeLine = Math.max(1, line); int safeColumn = Math.max(1, column); From 04485ff9020a1d3477d31d4018ed313803312974 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:46:22 +0300 Subject: [PATCH 28/38] Rewrite try-resource helper types to AutoCloseable --- .../main/java/com/codenameone/playground/PlaygroundRunner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 681dffd134..e80811522a 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -176,7 +176,10 @@ private String rewriteInlineAutoCloseableClasses(String script) { for (int i = 0; i < helperClassNames.size(); i++) { String className = helperClassNames.get(i); String classToken = escapeRegexLiteral(className); + RE resourceTypePattern = new RE("([\\(;]\\s*)" + classToken + "(\\s+[A-Za-z_$][A-Za-z0-9_$]*\\s*=)"); RE ctorPattern = new RE("\\bnew\\s+" + classToken + "\\s*\\(\\s*\\)"); + rewritten = resourceTypePattern.subst(rewritten, "$1AutoCloseable$2", + RE.REPLACE_ALL | RE.REPLACE_BACKREFERENCES); rewritten = ctorPattern.subst(rewritten, "(new AutoCloseable() { public void close() {} })", RE.REPLACE_ALL); rewritten = StringUtil.replaceAll(rewritten, "new " + className + "()", From f087fdfb6a374e193ba24db447a635d310cff57e Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:54:23 +0300 Subject: [PATCH 29/38] Stop rewriting inline AutoCloseable helpers in adaptScript --- .../main/java/com/codenameone/playground/PlaygroundRunner.java | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index e80811522a..28f0f39c31 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -148,7 +148,6 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; - normalized = rewriteInlineAutoCloseableClasses(normalized); normalized = rewriteKnownSamCalls(normalized); normalized = rewriteLambdaArguments(normalized); String wrapped = wrapLooseScript(normalized); From b2c8b94ded122d80a31d47a7aa2391718050c943 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 9 Apr 2026 22:13:15 +0300 Subject: [PATCH 30/38] Enable scripted class declarations in BSHClassDeclaration --- .../main/java/bsh/BSHClassDeclaration.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java b/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java index 1f60bbc3d4..4ee36db23f 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java +++ b/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java @@ -63,9 +63,22 @@ public synchronized Object eval(final CallStack callstack, final Interpreter int private Class generateClass(final CallStack callstack, final Interpreter interpreter) throws EvalError { - throw new EvalError( - "Class, interface, and enum declarations are not supported in the Codename One BeanShell runtime.", - this, callstack); + int child = 0; + Class superClass = null; + if (extend) { + BSHAmbiguousName superNode = (BSHAmbiguousName) jjtGetChild(child++); + superClass = superNode.toClass(callstack, interpreter); + } + + Class[] interfaces = new Class[numInterfaces]; + for (int i = 0; i < numInterfaces; i++) { + BSHAmbiguousName iface = (BSHAmbiguousName) jjtGetChild(child++); + interfaces[i] = iface.toClass(callstack, interpreter); + } + + BSHBlock body = (BSHBlock) jjtGetChild(child); + return ClassGenerator.getClassGenerator().generateClass( + name, modifiers, interfaces, superClass, body, type, callstack, interpreter); } public String toString() { From 7d0b3755678a1eff96581573ffde1f9e6b0d7818 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 07:36:54 +0300 Subject: [PATCH 31/38] Remove reflection/UUID/stream usage from class generation util --- .../src/main/java/bsh/ClassGenerator.java | 3 - .../src/main/java/bsh/ClassGeneratorUtil.java | 103 +----------------- 2 files changed, 6 insertions(+), 100 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index c46a56ff2d..9ea1e6aed0 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -129,9 +129,6 @@ public static Class generateClassImpl(String name, Modifiers modifiers, Class Interpreter.debug(classStaticNameSpace); - if (interpreter.getStrictJava()) - ClassGeneratorUtil.checkAbstractMethodImplementation(genClass); - return genClass; } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index 9262e7f222..d2e5daca60 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -38,12 +38,9 @@ import java.io.IOException; import java.io.Reader; -import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.UUID; import bsh.org.objectweb.asm.ClassWriter; import bsh.org.objectweb.asm.Label; @@ -139,7 +136,8 @@ public ClassGeneratorUtil(Modifiers classModifiers, String className, this.vars = vars; classStaticNameSpace.isInterface = type == INTERFACE; classStaticNameSpace.isEnum = type == ENUM; - This.contextStore.put(this.uuid = UUID.randomUUID().toString(), classStaticNameSpace); + this.uuid = className + "_" + Long.toString(System.currentTimeMillis(), 36); + This.contextStore.put(this.uuid, classStaticNameSpace); // Split the methods into constructors and regular method lists List consl = new ArrayList<>(); @@ -290,8 +288,8 @@ else if (type == ENUM) generateMethod(className, fqClassName, method.getName(), method.getReturnTypeDescriptor(), method.getParamTypeDescriptors(), modifiers, cw); - // check if method overrides existing method and generate super delegate. - if ( null != classContainsMethod(superClass, method.getName(), method.getParamTypeDescriptors()) && !isStatic ) + // Generate super delegate for instance methods. + if ( !isStatic ) generateSuperDelegateMethod(superClass, superClassName, method.getName(), method.getReturnTypeDescriptor(), method.getParamTypeDescriptors(), ACC_PUBLIC, cw); } @@ -717,98 +715,9 @@ private void generateSuperDelegateMethod(Class superClass, String superClassN cv.visitMaxs(0, 0); } - /** Validate abstract method implementation. - * Check that class is abstract or implements all abstract methods. - * BSH classes are not abstract which allows us to instantiate abstract - * classes. Also applies inheritance rules @see checkInheritanceRules(). - * @param type The class to check. - * @throws RuntimException if validation fails. */ + /** Strict-Java abstract checks are not supported in the CN1 playground runtime. */ static void checkAbstractMethodImplementation(Class type) { - final List meths = new ArrayList<>(); - class Reflector { - void gatherMethods(Class type) { - meths.addAll(Arrays.asList(type.getDeclaredMethods())); - for (Class i : type.getInterfaces()) - gatherMethods(i); - } - } - new Reflector().gatherMethods(type); - // for each filtered abstract method - meths.stream().filter( m -> ( m.getModifiers() & ACC_ABSTRACT ) > 0 ) - .forEach( method -> { - Method[] meth = meths.stream() - // find methods of the same name - .filter( m -> method.getName().equals(m.getName() ) - // not abstract nor private - && ( m.getModifiers() & (ACC_ABSTRACT|ACC_PRIVATE) ) == 0 - // with matching parameters - && Types.areSignaturesEqual( - method.getParameterTypes(), m.getParameterTypes())) - // sort most visible methods to the top - // comparator: -1 if a is public or b not public or protected - // 0 if access modifiers for a and b are equal - .sorted( (a, b) -> ( a.getModifiers() & ACC_PUBLIC ) > 0 - || ( b.getModifiers() & (ACC_PUBLIC|ACC_PROTECTED) ) == 0 - ? -1 : ( a.getModifiers() & ACCESS_MODIFIERS ) == - ( b.getModifiers() & ACCESS_MODIFIERS ) - ? 0 : 1 ) - .toArray(Method[]::new); - // with no overriding methods class must be abstract - if ( meth.length == 0 && !Reflect.getClassModifiers(type) - .hasModifier("abstract") ) - throw new RuntimeException(type.getSimpleName() - + " is not abstract and does not override abstract method " - + method.getName() + "() in " - + method.getDeclaringClass().getSimpleName()); - // apply inheritance rules to most visible method at index 0 - if ( meth.length > 0) - checkInheritanceRules(method.getModifiers(), - meth[0].getModifiers(), method.getDeclaringClass()); - }); - } - - /** Apply inheritance rules. Overridden methods may not reduce visibility. - * @param parentModifiers parent modifiers of method being overridden - * @param overriddenModifiers overridden modifiers of new method - * @param parentClass parent class name - * @return true if visibility is not reduced - * @throws RuntimeException if validation fails */ - static boolean checkInheritanceRules(int parentModifiers, int overriddenModifiers, Class parentClass) { - int prnt = parentModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); - int chld = overriddenModifiers & ( ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED ); - - if ( chld == prnt || prnt == ACC_PRIVATE || chld == ACC_PUBLIC || prnt == 0 && chld != ACC_PRIVATE ) - return true; - - throw new RuntimeException("Cannot reduce the visibility of the inherited method from " - + parentClass.getName()); - } - - /** Check if method name and type descriptor signature is overridden. - * @param clas super class - * @param methodName name of method - * @param paramTypes type descriptor of parameter types - * @return matching method or null if not found */ - static Method classContainsMethod(Class clas, String methodName, String[] paramTypes) { - if (clas == null) { - return null; - } - for (Method method : clas.getDeclaredMethods()) { - if (method.getName().equals(methodName) - && paramTypes.length == method.getParameterCount()) { - String[] methodParamTypes = getTypeDescriptors(method.getParameterTypes()); - boolean found = true; - for (int j = 0; j < paramTypes.length; j++) { - if (false == (found = paramTypes[j].equals(methodParamTypes[j]))) { - break; - } - } - if (found) { - return method; - } - } - } - return null; + // no-op } /** Generate return code for a normal bytecode From 890b25b382607a41198ff42fa8f321fab5ad46d3 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 07:36:58 +0300 Subject: [PATCH 32/38] Restore strict validation hook with faux-reflection checks --- .../src/main/java/bsh/ClassGenerator.java | 4 +++ .../src/main/java/bsh/ClassGeneratorUtil.java | 36 +++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index 9ea1e6aed0..c81f64b2a2 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -129,6 +129,10 @@ public static Class generateClassImpl(String name, Modifiers modifiers, Class Interpreter.debug(classStaticNameSpace); + if (interpreter.getStrictJava()) { + ClassGeneratorUtil.checkAbstractMethodImplementation(genClass); + } + return genClass; } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index d2e5daca60..c44942d1f4 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -715,9 +715,41 @@ private void generateSuperDelegateMethod(Class superClass, String superClassN cv.visitMaxs(0, 0); } - /** Strict-Java abstract checks are not supported in the CN1 playground runtime. */ + /** + * Strict-Java validation using BeanShell's faux-reflection descriptors. + */ static void checkAbstractMethodImplementation(Class type) { - // no-op + BshMethod[] methods = Reflect.getDeclaredMethods(type); + for (int i = 0; i < methods.length; i++) { + BshMethod parent = methods[i]; + if (!parent.hasModifier("abstract")) { + continue; + } + + BshMethod impl = null; + for (int j = 0; j < methods.length; j++) { + BshMethod candidate = methods[j]; + if (candidate == parent) { + continue; + } + if (!candidate.getName().equals(parent.getName())) { + continue; + } + if (candidate.hasModifier("abstract") || candidate.hasModifier("private")) { + continue; + } + if (Types.areSignaturesEqual(parent.getParameterTypes(), candidate.getParameterTypes())) { + impl = candidate; + break; + } + } + + if (impl == null && !Reflect.getClassModifiers(type).hasModifier("abstract")) { + throw new RuntimeException(type.getName() + + " is not abstract and does not implement " + + parent.getName() + "()"); + } + } } /** Generate return code for a normal bytecode From ebbbf11d13a9e671fb6d0f875803db33e21fb798 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 09:20:30 +0300 Subject: [PATCH 33/38] Remove ASM runtime usage and fall back to CN1-safe rewrites --- .../main/java/bsh/BSHClassDeclaration.java | 19 +- .../src/main/java/bsh/ClassGenerator.java | 288 +-- .../src/main/java/bsh/ClassGeneratorUtil.java | 945 +------- .../java/bsh/org/objectweb/asm/Attribute.java | 290 --- .../bsh/org/objectweb/asm/ByteVector.java | 360 --- .../bsh/org/objectweb/asm/ClassVisitor.java | 235 -- .../bsh/org/objectweb/asm/ClassWriter.java | 757 ------ .../java/bsh/org/objectweb/asm/Constants.java | 175 -- .../bsh/org/objectweb/asm/CurrentFrame.java | 56 - .../main/java/bsh/org/objectweb/asm/Edge.java | 91 - .../bsh/org/objectweb/asm/FieldVisitor.java | 98 - .../bsh/org/objectweb/asm/FieldWriter.java | 238 -- .../java/bsh/org/objectweb/asm/Frame.java | 1467 ------------ .../java/bsh/org/objectweb/asm/Handle.java | 189 -- .../java/bsh/org/objectweb/asm/Handler.java | 198 -- .../java/bsh/org/objectweb/asm/LICENSE.txt | 29 - .../java/bsh/org/objectweb/asm/Label.java | 621 ----- .../bsh/org/objectweb/asm/MethodVisitor.java | 609 ----- .../bsh/org/objectweb/asm/MethodWriter.java | 2068 ----------------- .../java/bsh/org/objectweb/asm/Opcodes.java | 339 --- .../java/bsh/org/objectweb/asm/Symbol.java | 240 -- .../bsh/org/objectweb/asm/SymbolTable.java | 1121 --------- .../main/java/bsh/org/objectweb/asm/Type.java | 906 -------- .../java/bsh/org/objectweb/asm/TypePath.java | 201 -- .../bsh/org/objectweb/asm/TypeReference.java | 436 ---- .../playground/PlaygroundRunner.java | 7 +- .../PlaygroundSyntaxMatrixHarness.java | 4 +- 27 files changed, 43 insertions(+), 11944 deletions(-) delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Attribute.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ByteVector.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassVisitor.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassWriter.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Constants.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/CurrentFrame.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Edge.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldVisitor.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldWriter.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Frame.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handle.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handler.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/LICENSE.txt delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Label.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodVisitor.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodWriter.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Opcodes.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Symbol.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/SymbolTable.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Type.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypePath.java delete mode 100644 scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypeReference.java diff --git a/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java b/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java index 4ee36db23f..1f60bbc3d4 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java +++ b/scripts/cn1playground/common/src/main/java/bsh/BSHClassDeclaration.java @@ -63,22 +63,9 @@ public synchronized Object eval(final CallStack callstack, final Interpreter int private Class generateClass(final CallStack callstack, final Interpreter interpreter) throws EvalError { - int child = 0; - Class superClass = null; - if (extend) { - BSHAmbiguousName superNode = (BSHAmbiguousName) jjtGetChild(child++); - superClass = superNode.toClass(callstack, interpreter); - } - - Class[] interfaces = new Class[numInterfaces]; - for (int i = 0; i < numInterfaces; i++) { - BSHAmbiguousName iface = (BSHAmbiguousName) jjtGetChild(child++); - interfaces[i] = iface.toClass(callstack, interpreter); - } - - BSHBlock body = (BSHBlock) jjtGetChild(child); - return ClassGenerator.getClassGenerator().generateClass( - name, modifiers, interfaces, superClass, body, type, callstack, interpreter); + throw new EvalError( + "Class, interface, and enum declarations are not supported in the Codename One BeanShell runtime.", + this, callstack); } public String toString() { diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java index c81f64b2a2..d1f4a1488f 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGenerator.java @@ -15,282 +15,54 @@ * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * - * * - * * - * This file is part of the BeanShell Java Scripting distribution. * - * Documentation and updates may be found at http://www.beanshell.org/ * - * Patrick Niemeyer (pat@pat.net) * - * Author of Learning Java, O'Reilly & Associates * - * * *****************************************************************************/ -package bsh; - -import static bsh.This.Keys.BSHSUPER; -import java.util.ArrayList; -import java.util.List; +package bsh; +/** + * Scripted class generation is not supported in the CN1 playground runtime. + * This stub preserves parser/runtime references while failing explicitly if a + * script attempts to declare or synthesize classes. + */ public final class ClassGenerator { - - enum Type { CLASS, INTERFACE, ENUM } - - private static ClassGenerator cg; - - public static ClassGenerator getClassGenerator() { - if (cg == null) { - cg = new ClassGenerator(); - } - - return cg; - } - - /** - * Parse the BSHBlock for the class definition and generate the class. - */ - public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { - // Delegate to the static method - return generateClassImpl(name, modifiers, interfaces, superClass, block, type, callstack, interpreter); - } - - /** - * Invoke a super.method() style superclass method on an object instance. - * This is not a normal function of the Java reflection API and is - * provided by generated class accessor methods. - */ - public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, Class classStatic, String methodName, Object[] args) throws UtilEvalError, ReflectError { - // Delegate to the static method - return invokeSuperclassMethodImpl(bcm, instance, classStatic, methodName, args); + public enum Type { + CLASS, INTERFACE, ENUM } - /** - * Parse the BSHBlock for for the class definition and generate the class - * using ClassGenerator. - */ - public static Class generateClassImpl(String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, Type type, CallStack callstack, Interpreter interpreter) throws EvalError { - NameSpace enclosingNameSpace = callstack.top(); - String packageName = enclosingNameSpace.getPackage(); - String className = enclosingNameSpace.isClass ? (enclosingNameSpace.getName() + "$" + name) : name; - String fqClassName = packageName == null ? className : packageName + "." + className; - BshClassManager bcm = interpreter.getClassManager(); - - // Create the class static namespace - NameSpace classStaticNameSpace = new NameSpace(enclosingNameSpace, className); - classStaticNameSpace.isClass = true; + public static final class ClassNodeFilter implements BSHBlock.NodeFilter { + public static final ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(); + public static final ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(); - callstack.push(classStaticNameSpace); - - // Evaluate any inner class class definitions in the block - // effectively recursively call this method for contained classes first - block.evalBlock(callstack, interpreter, true/*override*/, ClassNodeFilter.CLASSCLASSES); - - // Generate the type for our class - Variable[] variables = getDeclaredVariables(block, callstack, interpreter, packageName); - DelayedEvalBshMethod[] methods = getDeclaredMethods(block, callstack, interpreter, packageName, superClass); - - callstack.pop(); - - // initialize static this singleton in namespace - classStaticNameSpace.getThis(interpreter); - - // Create the class generator, which encapsulates all knowledge of the - // structure of the class - ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(modifiers, className, packageName, superClass, interfaces, variables, methods, classStaticNameSpace, type); - - // Let the class generator install hooks relating to the structure of - // the class into the class static namespace. e.g. the constructor - // array. This is necessary whether we are generating code or just - // reinitializing a previously generated class. - classGenerator.initStaticNameSpace(classStaticNameSpace, block/*instance initializer*/); - - // Check for existing class (saved class file) - Class genClass = bcm.getAssociatedClass(fqClassName); - - // If the class isn't there then generate it. - // Else just let it be initialized below. - if (genClass == null) { - // generate bytecode, optionally with static init hooks to - // bootstrap the interpreter - byte[] code = classGenerator.generateClass(); - - if (Interpreter.getSaveClasses()) { - Interpreter.debug("Class file persistence is not available in the Codename One playground runtime."); - } - - // Define the new class in the classloader - genClass = bcm.defineClass(fqClassName, code); - Interpreter.debug("Define ", fqClassName, " as ", genClass); + private ClassNodeFilter() { } - // import the unqualified class name into parent namespace - enclosingNameSpace.importClass(fqClassName.replace('$', '.')); - - // Give the static space its class static import - // important to do this after all classes are defined - classStaticNameSpace.setClassStatic(genClass); - Interpreter.debug(classStaticNameSpace); - - if (interpreter.getStrictJava()) { - ClassGeneratorUtil.checkAbstractMethodImplementation(genClass); + public boolean isVisible(Node node) { + return false; } - - return genClass; } - static Variable[] getDeclaredVariables(BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) { - List vars = new ArrayList(); - for (int child = 0; child < body.jjtGetNumChildren(); child++) { - Node node = body.jjtGetChild(child); - if (node instanceof BSHEnumConstant) { - BSHEnumConstant enm = (BSHEnumConstant) node; - try { - Variable var = new Variable(enm.getName(), - enm.getType(), null/*value*/, enm.mods); - vars.add(var); - } catch (UtilEvalError e) { - // value error shouldn't happen - } - } else if (node instanceof BSHTypedVariableDeclaration) { - BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node; - Modifiers modifiers = tvd.modifiers; - BSHVariableDeclarator[] vardec = tvd.getDeclarators(); - for (BSHVariableDeclarator aVardec : vardec) { - String name = aVardec.name; - try { - Class type = tvd.evalType(callstack, interpreter); - Variable var = new Variable(name, type, null/*value*/, modifiers); - vars.add(var); - } catch (UtilEvalError | EvalError e) { - // value error shouldn't happen - } - } - } - } + private static final ClassGenerator INSTANCE = new ClassGenerator(); - return vars.toArray(new Variable[vars.size()]); + private ClassGenerator() { } - static DelayedEvalBshMethod[] getDeclaredMethods(BSHBlock body, - CallStack callstack, Interpreter interpreter, String defaultPackage, - Class superClass) throws EvalError { - List methods = new ArrayList<>(); - if ( callstack.top().getName().indexOf("$anon") > -1 ) { - // anonymous classes need super constructor - String classBaseName = Types.getBaseName(callstack.top().getName()); - Invocable con = BshClassManager.memberCache.get(superClass) - .findMethod(superClass.getName(), - This.CONTEXT_ARGS.get().get(classBaseName)); - DelayedEvalBshMethod bm = new DelayedEvalBshMethod(classBaseName, con, callstack.top()); - methods.add(bm); - } - for (int child = 0; child < body.jjtGetNumChildren(); child++) { - Node node = body.jjtGetChild(child); - if (node instanceof BSHMethodDeclaration) { - BSHMethodDeclaration md = (BSHMethodDeclaration) node; - md.insureNodesParsed(); - Modifiers modifiers = md.modifiers; - String name = md.name; - String returnType = md.getReturnTypeDescriptor(callstack, interpreter, defaultPackage); - BSHReturnType returnTypeNode = md.getReturnTypeNode(); - BSHFormalParameters paramTypesNode = md.paramsNode; - String[] paramTypes = paramTypesNode.getTypeDescriptors(callstack, interpreter, defaultPackage); - - DelayedEvalBshMethod bm = new DelayedEvalBshMethod(name, returnType, returnTypeNode, md.paramsNode.getParamNames(), paramTypes, paramTypesNode, md.blockNode, null/*declaringNameSpace*/, modifiers, md.isVarArgs, callstack, interpreter); - - methods.add(bm); - } - } - return methods.toArray(new DelayedEvalBshMethod[methods.size()]); + public static ClassGenerator getClassGenerator() { + return INSTANCE; } - - /** - * A node filter that filters nodes for either a class body static - * initializer or instance initializer. In the static case only static - * members are passed, etc. - */ - static class ClassNodeFilter implements BSHBlock.NodeFilter { - private enum Context { STATIC, INSTANCE, CLASSES } - private enum Types { ALL, METHODS, FIELDS } - public static ClassNodeFilter CLASSSTATICFIELDS = new ClassNodeFilter(Context.STATIC, Types.FIELDS); - public static ClassNodeFilter CLASSSTATICMETHODS = new ClassNodeFilter(Context.STATIC, Types.METHODS); - public static ClassNodeFilter CLASSINSTANCEFIELDS = new ClassNodeFilter(Context.INSTANCE, Types.FIELDS); - public static ClassNodeFilter CLASSINSTANCEMETHODS = new ClassNodeFilter(Context.INSTANCE, Types.METHODS); - public static ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(Context.CLASSES); - - Context context; - Types types = Types.ALL; - - private ClassNodeFilter(Context context) { - this.context = context; - } - - private ClassNodeFilter(Context context, Types types) { - this.context = context; - this.types = types; - } - - @Override - public boolean isVisible(Node node) { - if (context == Context.CLASSES) return node instanceof BSHClassDeclaration; - - // Only show class decs in CLASSES - if (node instanceof BSHClassDeclaration) return false; - - if (context == Context.STATIC) - return types == Types.METHODS ? isStaticMethod(node) : isStatic(node); - - // context == Context.INSTANCE cannot be anything else - return types == Types.METHODS ? isInstanceMethod(node) : isNonStatic(node); - } - - private boolean isStatic(Node node) { - if ( node.jjtGetParent().jjtGetParent() instanceof BSHClassDeclaration - && ((BSHClassDeclaration) node.jjtGetParent().jjtGetParent()).type == Type.INTERFACE ) - return true; - - if (node instanceof BSHTypedVariableDeclaration) - return ((BSHTypedVariableDeclaration) node).modifiers.hasModifier("static"); - - if (node instanceof BSHBlock) - return ((BSHBlock) node).isStatic; - - return false; - } - - private boolean isNonStatic(Node node) { - if (node instanceof BSHMethodDeclaration) - return false; - return !isStatic(node); - } - - private boolean isStaticMethod(Node node) { - if (node instanceof BSHMethodDeclaration) - return ((BSHMethodDeclaration) node).modifiers.hasModifier("static"); - return false; - } - - private boolean isInstanceMethod(Node node) { - if (node instanceof BSHMethodDeclaration) - return !((BSHMethodDeclaration) node).modifiers.hasModifier("static"); - return false; - } + public Class generateClass(String name, Modifiers modifiers, Class[] interfaces, + Class superClass, BSHBlock block, Type type, CallStack callstack, + Interpreter interpreter) throws EvalError { + throw new EvalError("Scripted class generation is not supported in the Codename One BeanShell runtime.", + null, callstack); } - /** Find and invoke the super class delegate method. */ - public static Object invokeSuperclassMethodImpl(BshClassManager bcm, - Object instance, Class classStatic, String methodName, Object[] args) - throws UtilEvalError, ReflectError { - Class clas = instance.getClass(); - String superName = BSHSUPER + methodName; - - // look for the specially named super delegate method - Invocable superMethod = Reflect.resolveJavaMethod(clas, superName, - Types.getTypes(args), false/*onlyStatic*/); - if (superMethod != null) { - return superMethod.invoke(instance, args); - } - - throw new ReflectError("Superclass method delegate not found: " + methodName); + public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, + Class superClass, String methodName, Object[] args) throws EvalError { + throw new EvalError("Superclass dispatch for generated classes is not supported in the Codename One BeanShell runtime.", + null, null); } - } diff --git a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java index c44942d1f4..8483355285 100644 --- a/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java +++ b/scripts/cn1playground/common/src/main/java/bsh/ClassGeneratorUtil.java @@ -1,948 +1,11 @@ -/***************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - * * - * * - * This file is part of the BeanShell Java Scripting distribution. * - * Documentation and updates may be found at http://www.beanshell.org/ * - * Patrick Niemeyer (pat@pat.net) * - * Author of Learning Java, O'Reilly & Associates * - * * - *****************************************************************************/ - package bsh; -import static bsh.ClassGenerator.Type.CLASS; -import static bsh.ClassGenerator.Type.ENUM; -import static bsh.ClassGenerator.Type.INTERFACE; -import static bsh.This.Keys.BSHCLASSMODIFIERS; -import static bsh.This.Keys.BSHCONSTRUCTORS; -import static bsh.This.Keys.BSHINIT; -import static bsh.This.Keys.BSHSTATIC; -import static bsh.This.Keys.BSHSUPER; -import static bsh.This.Keys.BSHTHIS; - -import java.io.IOException; -import java.io.Reader; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import bsh.org.objectweb.asm.ClassWriter; -import bsh.org.objectweb.asm.Label; -import bsh.org.objectweb.asm.MethodVisitor; -import bsh.org.objectweb.asm.Opcodes; -import bsh.org.objectweb.asm.Type; - /** - * ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator - * by Eric Bruneton in order to generate class "stubs" for BeanShell at - * runtime. - *

- *

- * Stub classes contain all of the fields of a BeanShell scripted class - * as well as two "callback" references to BeanShell namespaces: one for - * static methods and one for instance methods. Methods of the class are - * delegators which invoke corresponding methods on either the static or - * instance bsh object and then unpack and return the results. The static - * namespace utilizes a static import to delegate variable access to the - * class' static fields. The instance namespace utilizes a dynamic import - * (i.e. mixin) to delegate variable access to the class' instance variables. - *

- *

- * Constructors for the class delegate to the static initInstance() method of - * ClassGeneratorUtil to initialize new instances of the object. initInstance() - * invokes the instance intializer code (init vars and instance blocks) and - * then delegates to the corresponding scripted constructor method in the - * instance namespace. Constructors contain special switch logic which allows - * the BeanShell to control the calling of alternate constructors (this() or - * super() references) at runtime. - *

- *

- * Specially named superclass delegator methods are also generated in order to - * allow BeanShell to access overridden methods of the superclass (which - * reflection does not normally allow). - *

- * - * @author Pat Niemeyer + * Placeholder for the disabled class-generation pipeline. */ -public class ClassGeneratorUtil implements Opcodes { - /** - * The switch branch number for the default constructor. - * The value -1 will cause the default branch to be taken. - */ - static final int DEFAULTCONSTRUCTOR = -1; - static final int ACCESS_MODIFIERS = - ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; - - private static final String OBJECT = "Ljava/lang/Object;"; - - private final String className; - private final String classDescript; - /** - * fully qualified class name (with package) e.g. foo/bar/Blah - */ - private final String fqClassName; - private final String uuid; - private final Class superClass; - private final String superClassName; - private final Class[] interfaces; - private final Variable[] vars; - private final DelayedEvalBshMethod[] constructors; - private final DelayedEvalBshMethod[] methods; - private final Modifiers classModifiers; - private final ClassGenerator.Type type; - - /** - * @param packageName e.g. "com.foo.bar" - */ - public ClassGeneratorUtil(Modifiers classModifiers, String className, - String packageName, Class superClass, Class[] interfaces, - Variable[] vars, DelayedEvalBshMethod[] bshmethods, - NameSpace classStaticNameSpace, ClassGenerator.Type type) { - this.classModifiers = classModifiers; - this.className = className; - this.type = type; - if (packageName != null) - this.fqClassName = packageName.replace('.', '/') + "/" + className; - else - this.fqClassName = className; - this.classDescript = "L"+fqClassName.replace('.', '/')+";"; - - if (superClass == null) - if (type == ENUM) - superClass = Enum.class; - else - superClass = Object.class; - this.superClass = superClass; - this.superClassName = Type.getInternalName(superClass); - if (interfaces == null) - interfaces = Reflect.ZERO_TYPES; - this.interfaces = interfaces; - this.vars = vars; - classStaticNameSpace.isInterface = type == INTERFACE; - classStaticNameSpace.isEnum = type == ENUM; - this.uuid = className + "_" + Long.toString(System.currentTimeMillis(), 36); - This.contextStore.put(this.uuid, classStaticNameSpace); - - // Split the methods into constructors and regular method lists - List consl = new ArrayList<>(); - List methodsl = new ArrayList<>(); - String classBaseName = Types.getBaseName(className); // for inner classes - for (DelayedEvalBshMethod bshmethod : bshmethods) - if (bshmethod.getName().equals(classBaseName)) { - if (!bshmethod.modifiers.isAppliedContext(Modifiers.CONSTRUCTOR)) - bshmethod.modifiers.changeContext(Modifiers.CONSTRUCTOR); - consl.add(bshmethod); - } else - methodsl.add(bshmethod); - - constructors = consl.toArray(new DelayedEvalBshMethod[consl.size()]); - methods = methodsl.toArray(new DelayedEvalBshMethod[methodsl.size()]); - - Interpreter.debug("Generate class ", type, " ", fqClassName, " cons:", - consl.size(), " meths:", methodsl.size(), " vars:", vars.length); - - if (type == INTERFACE && !classModifiers.hasModifier("abstract")) - classModifiers.addModifier("abstract"); - if (type == ENUM && !classModifiers.hasModifier("static")) - classModifiers.addModifier("static"); - } - - /** - * This method provides a hook for the class generator implementation to - * store additional information in the class's bsh static namespace. - * Currently this is used to store an array of consructors corresponding - * to the constructor switch in the generated class. - * - * This method must be called to initialize the static space even if we - * are using a previously generated class. - */ - public void initStaticNameSpace(NameSpace classStaticNameSpace, BSHBlock instanceInitBlock) { - try { - classStaticNameSpace.setLocalVariable(""+BSHCLASSMODIFIERS, classModifiers, false/*strict*/); - classStaticNameSpace.setLocalVariable(""+BSHCONSTRUCTORS, constructors, false/*strict*/); - classStaticNameSpace.setLocalVariable(""+BSHINIT, instanceInitBlock, false/*strict*/); - } catch (UtilEvalError e) { - throw new InterpreterError("Unable to init class static block: " + e, e); - } - } - - /** - * Generate the class bytecode for this class. - */ - public byte[] generateClass() { - NameSpace classStaticNameSpace = This.contextStore.get(this.uuid); - // Force the class public for now... - int classMods = getASMModifiers(classModifiers) | ACC_PUBLIC; - if (type == INTERFACE) - classMods |= ACC_INTERFACE | ACC_ABSTRACT; - else if (type == ENUM) - classMods |= ACC_FINAL | ACC_SUPER | ACC_ENUM; - else { - classMods |= ACC_SUPER; - if ( (classMods & ACC_ABSTRACT) > 0 ) - // bsh classes are not abstract - classMods -= ACC_ABSTRACT; - } - - String[] interfaceNames = new String[interfaces.length + 1]; // +1 for GeneratedClass - for (int i = 0; i < interfaces.length; i++) { - interfaceNames[i] = Type.getInternalName(interfaces[i]); - if (Reflect.isGeneratedClass(interfaces[i])) - for (Variable v : Reflect.getVariables(interfaces[i])) - classStaticNameSpace.setVariableImpl(v); - } - // Everyone implements GeneratedClass - interfaceNames[interfaces.length] = Type.getInternalName(GeneratedClass.class); - - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - String signature = type == ENUM ? "Ljava/lang/Enum<"+classDescript+">;" : null; - cw.visit(V1_8, classMods, fqClassName, signature, superClassName, interfaceNames); - - if ( type != INTERFACE ) - // Generate the bsh instance 'This' reference holder field - generateField(BSHTHIS+className, "Lbsh/This;", ACC_PUBLIC, cw); - // Generate the static bsh static This reference holder field - generateField(BSHSTATIC+className, "Lbsh/This;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, cw); - // Generate class UUID - generateField("UUID", "Ljava/lang/String;", ACC_PUBLIC + ACC_STATIC + ACC_FINAL, this.uuid, cw); - - // Generate the fields - for (Variable var : vars) { - // Don't generate private fields - if (var.hasModifier("private")) - continue; - - String fType = var.getTypeDescriptor(); - int modifiers = getASMModifiers(var.getModifiers()); - - if ( type == INTERFACE ) { - var.setConstant(); - classStaticNameSpace.setVariableImpl(var); - // keep constant fields virtual - continue; - } else if ( type == ENUM && var.hasModifier("enum") ) { - modifiers |= ACC_ENUM | ACC_FINAL; - fType = classDescript; - } - - generateField(var.getName(), fType, modifiers, cw); - } - - if (type == ENUM) - generateEnumSupport(fqClassName, className, classDescript, cw); - - // Generate the static initializer. - generateStaticInitializer(cw); - - // Generate the constructors - boolean hasConstructor = false; - for (int i = 0; i < constructors.length; i++) { - // Don't generate private constructors - if (constructors[i].hasModifier("private")) - continue; - - int modifiers = getASMModifiers(constructors[i].getModifiers()); - if (constructors[i].isVarArgs()) - modifiers |= ACC_VARARGS; - generateConstructor(i, constructors[i].getParamTypeDescriptors(), modifiers, cw); - hasConstructor = true; - } - - // If no other constructors, generate a default constructor - if ( type == CLASS && !hasConstructor ) - generateConstructor(DEFAULTCONSTRUCTOR/*index*/, new String[0], ACC_PUBLIC, cw); - - // Generate methods - for (DelayedEvalBshMethod method : methods) { - - // Don't generate private methods - if (method.hasModifier("private")) - continue; - - if ( type == INTERFACE - && !method.hasModifier("static") - && !method.hasModifier("default") - && !method.hasModifier("abstract") ) - method.getModifiers().addModifier("abstract"); - int modifiers = getASMModifiers(method.getModifiers()); - if (method.isVarArgs()) - modifiers |= ACC_VARARGS; - boolean isStatic = (modifiers & ACC_STATIC) > 0; - - generateMethod(className, fqClassName, method.getName(), method.getReturnTypeDescriptor(), - method.getParamTypeDescriptors(), modifiers, cw); - - // Generate super delegate for instance methods. - if ( !isStatic ) - generateSuperDelegateMethod(superClass, superClassName, method.getName(), method.getReturnTypeDescriptor(), - method.getParamTypeDescriptors(), ACC_PUBLIC, cw); - } - - return cw.toByteArray(); - } - - /** - * Translate bsh.Modifiers into ASM modifier bitflags. - * Only a subset of modifiers are baked into classes. - */ - private static int getASMModifiers(Modifiers modifiers) { - int mods = 0; - - if (modifiers.hasModifier(ACC_PUBLIC)) - mods |= ACC_PUBLIC; - if (modifiers.hasModifier(ACC_PRIVATE)) - mods |= ACC_PRIVATE; - if (modifiers.hasModifier(ACC_PROTECTED)) - mods |= ACC_PROTECTED; - if (modifiers.hasModifier(ACC_STATIC)) - mods |= ACC_STATIC; - if (modifiers.hasModifier(ACC_SYNCHRONIZED)) - mods |= ACC_SYNCHRONIZED; - if (modifiers.hasModifier(ACC_ABSTRACT)) - mods |= ACC_ABSTRACT; - - // if no access modifiers declared then we make it public - if ( ( modifiers.getModifiers() & ACCESS_MODIFIERS ) == 0 ) { - mods |= ACC_PUBLIC; - modifiers.addModifier(ACC_PUBLIC); - } - - return mods; - } - - /** Generate a field - static or instance. */ - private static void generateField(String fieldName, String type, int modifiers, ClassWriter cw) { - generateField(fieldName, type, modifiers, null/*value*/, cw); - } - /** Generate field and assign initial value. */ - private static void generateField(String fieldName, String type, int modifiers, Object value, ClassWriter cw) { - cw.visitField(modifiers, fieldName, type, null/*signature*/, value); - } - - /** - * Build the signature for the supplied parameter types. - * @param paramTypes list of parameter types - * @return parameter type signature - */ - private static String getTypeParameterSignature(String[] paramTypes) { - StringBuilder sb = new StringBuilder("<"); - for (final String pt : paramTypes) - sb.append(pt).append(":"); - return sb.toString(); - } - - /** Generate support code needed for Enum types. - * Generates enum values and valueOf methods, default private constructor with initInstance call. - * Instead of maintaining a synthetic array of enum values we greatly reduce the required bytecode - * needed by delegating to This.enumValues and building the array dynamically. - * @param fqClassName fully qualified class name - * @param className class name string - * @param classDescript class descriptor string - * @param cw current class writer */ - private void generateEnumSupport(String fqClassName, String className, String classDescript, ClassWriter cw) { - // generate enum values() method delegated to static This.enumValues. - MethodVisitor cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "values", "()["+classDescript, null, null); - pushBshStatic(fqClassName, className, cv); - cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "enumValues", "()[Ljava/lang/Object;", false); - generatePlainReturnCode("["+classDescript, cv); - cv.visitMaxs(0, 0); - // generate Enum.valueOf delegate method - cv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "valueOf", "(Ljava/lang/String;)"+classDescript, null, null); - cv.visitLdcInsn(Type.getType(classDescript)); - cv.visitVarInsn(ALOAD, 0); - cv.visitMethodInsn(INVOKESTATIC, "java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false); - generatePlainReturnCode(classDescript, cv); - cv.visitMaxs(0, 0); - // generate default private constructor and initInstance call - cv = cw.visitMethod(ACC_PRIVATE, "", "(Ljava/lang/String;I)V", null, null); - cv.visitVarInsn(ALOAD, 0); - cv.visitVarInsn(ALOAD, 1); - cv.visitVarInsn(ILOAD, 2); - cv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "", "(Ljava/lang/String;I)V", false); - cv.visitVarInsn(ALOAD, 0); - cv.visitLdcInsn(className); - generateParameterReifierCode(new String[0], false/*isStatic*/, cv); - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); - cv.visitInsn(RETURN); - cv.visitMaxs(0, 0); - } - - /** Generate the static initialization of the enum constants. Called from clinit. - * @param fqClassName fully qualified class name - * @param classDescript class descriptor string - * @param cv clinit method visitor */ - private void generateEnumStaticInit(String fqClassName, String classDescript, MethodVisitor cv) { - int ordinal = ICONST_0; - for ( Variable var : vars ) if ( var.hasModifier("enum") ) { - cv.visitTypeInsn(NEW, fqClassName); - cv.visitInsn(DUP); - cv.visitLdcInsn(var.getName()); - if ( ICONST_5 >= ordinal ) - cv.visitInsn(ordinal++); - else - cv.visitIntInsn(BIPUSH, ordinal++ - ICONST_0); - cv.visitMethodInsn(INVOKESPECIAL, fqClassName, "", "(Ljava/lang/String;I)V", false); - cv.visitFieldInsn(PUTSTATIC, fqClassName, var.getName(), classDescript); - } - } - - /** - * Generate a delegate method - static or instance. - * The generated code packs the method arguments into an object array - * (wrapping primitive types in bsh.Primitive), invokes the static or - * instance This invokeMethod() method, and then returns - * the result. - */ - private void generateMethod(String className, String fqClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { - String[] exceptions = null; - boolean isStatic = (modifiers & ACC_STATIC) != 0; - - if (returnType == null) // map loose return type to Object - returnType = OBJECT; - - String methodDescriptor = getMethodDescriptor(returnType, paramTypes); - - String paramTypesSig = getTypeParameterSignature(paramTypes); - - // Generate method body - MethodVisitor cv = cw.visitMethod(modifiers, methodName, methodDescriptor, paramTypesSig, exceptions); - - if ((modifiers & ACC_ABSTRACT) != 0) - return; - - // Generate code to push the BSHTHIS or BSHSTATIC field - if ( isStatic||type == INTERFACE ) - pushBshStatic(fqClassName, className, cv); - else - pushBshThis(fqClassName, className, cv); - - // Push the name of the method as a constant - cv.visitLdcInsn(methodName); - - // Generate code to push arguments as an object array - generateParameterReifierCode(paramTypes, isStatic, cv); - - // Push the boolean constant 'true' (for declaredOnly) - cv.visitInsn(ICONST_1); - - // Invoke the method This.invokeMethod( name, Class [] sig, boolean ) - cv.visitMethodInsn(INVOKEVIRTUAL, "bsh/This", "invokeMethod", "(Ljava/lang/String;[Ljava/lang/Object;Z)Ljava/lang/Object;", false); - - // Generate code to return the value - generateReturnCode(returnType, cv); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Generate a constructor. - */ - void generateConstructor(int index, String[] paramTypes, int modifiers, ClassWriter cw) { - /** offset after params of the args object [] var */ - final int argsVar = paramTypes.length + 1; - /** offset after params of the ConstructorArgs var */ - final int consArgsVar = paramTypes.length + 2; - - String[] exceptions = null; - String methodDescriptor = getMethodDescriptor("V", paramTypes); - - String paramTypesSig = getTypeParameterSignature(paramTypes); - - // Create this constructor method - MethodVisitor cv = cw.visitMethod(modifiers, "", methodDescriptor, paramTypesSig, exceptions); - - // Generate code to push arguments as an object array - generateParameterReifierCode(paramTypes, false/*isStatic*/, cv); - cv.visitVarInsn(ASTORE, argsVar); - - // Generate the code implementing the alternate constructor switch - generateConstructorSwitch(index, argsVar, consArgsVar, cv); - - // Generate code to invoke the ClassGeneratorUtil initInstance() method - - // push 'this' - cv.visitVarInsn(ALOAD, 0); - - // Push the class/constructor name as a constant - cv.visitLdcInsn(className); - - // Push arguments as an object array - cv.visitVarInsn(ALOAD, argsVar); - - // invoke the initInstance() method - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initInstance", "(Lbsh/GeneratedClass;Ljava/lang/String;[Ljava/lang/Object;)V", false); - - cv.visitInsn(RETURN); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Generate the static initializer for the class - */ - void generateStaticInitializer(ClassWriter cw) { - - // Generate code to invoke the ClassGeneratorUtil initStatic() method - MethodVisitor cv = cw.visitMethod(ACC_STATIC, "", "()V", null/*sig*/, null/*exceptions*/); - - // initialize _bshStaticThis - cv.visitFieldInsn(GETSTATIC, fqClassName, "UUID", "Ljava/lang/String;"); - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "pullBshStatic", "(Ljava/lang/String;)Lbsh/This;", false); - cv.visitFieldInsn(PUTSTATIC, fqClassName, BSHSTATIC+className, "Lbsh/This;"); - - if ( type == ENUM ) - generateEnumStaticInit(fqClassName, classDescript, cv); - - // equivalent of my.ClassName.class - cv.visitLdcInsn(Type.getType(classDescript)); - - // invoke the initStatic() method - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "initStatic", "(Ljava/lang/Class;)V", false); - - cv.visitInsn(RETURN); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Generate a switch with a branch for each possible alternate - * constructor. This includes all superclass constructors and all - * constructors of this class. The default branch of this switch is the - * default superclass constructor. - *

- * This method also generates the code to call the static - * ClassGeneratorUtil - * getConstructorArgs() method which inspects the scripted constructor to - * find the alternate constructor signature (if any) and evaluate the - * arguments at runtime. The getConstructorArgs() method returns the - * actual arguments as well as the index of the constructor to call. - */ - void generateConstructorSwitch(int consIndex, int argsVar, int consArgsVar, - MethodVisitor cv) { - Label defaultLabel = new Label(); - Label endLabel = new Label(); - List superConstructors = BshClassManager.memberCache - .get(superClass).members(superClass.getName()); - int cases = superConstructors.size() + constructors.length; - - Label[] labels = new Label[cases]; - for (int i = 0; i < cases; i++) - labels[i] = new Label(); - - // Generate code to call ClassGeneratorUtil to get our switch index - // and give us args... - - // push super class name .class - cv.visitLdcInsn(Type.getType(BSHType.getTypeDescriptor(superClass))); - - // Push the bsh static namespace field - pushBshStatic(fqClassName, className, cv); - - // push args - cv.visitVarInsn(ALOAD, argsVar); - - // push this constructor index number onto stack - cv.visitIntInsn(BIPUSH, consIndex); - - // invoke the ClassGeneratorUtil getConstructorsArgs() method - cv.visitMethodInsn(INVOKESTATIC, "bsh/This", "getConstructorArgs", "(Ljava/lang/Class;Lbsh/This;[Ljava/lang/Object;I)" + "Lbsh/This$ConstructorArgs;", false); - - // store ConstructorArgs in consArgsVar - cv.visitVarInsn(ASTORE, consArgsVar); - - // Get the ConstructorArgs selector field from ConstructorArgs - - // push ConstructorArgs - cv.visitVarInsn(ALOAD, consArgsVar); - cv.visitFieldInsn(GETFIELD, "bsh/This$ConstructorArgs", "selector", "I"); - - // start switch - cv.visitTableSwitchInsn(0/*min*/, cases - 1/*max*/, defaultLabel, labels); - - // generate switch body - int index = 0; - for (int i = 0; i < superConstructors.size(); i++, index++) - doSwitchBranch(index, superClassName, superConstructors.get(i).getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); - for (int i = 0; i < constructors.length; i++, index++) - doSwitchBranch(index, fqClassName, constructors[i].getParamTypeDescriptors(), endLabel, labels, consArgsVar, cv); - - // generate the default branch of switch - cv.visitLabel(defaultLabel); - // default branch always invokes no args super - cv.visitVarInsn(ALOAD, 0); // push 'this' - cv.visitMethodInsn(INVOKESPECIAL, superClassName, "", "()V", false); - - // done with switch - cv.visitLabel(endLabel); - } - - // push the class static This object - private static void pushBshStatic(String fqClassName, String className, MethodVisitor cv) { - cv.visitFieldInsn(GETSTATIC, fqClassName, BSHSTATIC + className, "Lbsh/This;"); - } - - // push the class instance This object - private static void pushBshThis(String fqClassName, String className, MethodVisitor cv) { - // Push 'this' - cv.visitVarInsn(ALOAD, 0); - // Get the instance field - cv.visitFieldInsn(GETFIELD, fqClassName, BSHTHIS + className, "Lbsh/This;"); - } - - /** Generate a branch of the constructor switch. - * This method is called by generateConstructorSwitch. The code generated by this method assumes - * that the argument array is on the stack. - * @param index label index - * @param targetClassName class name - * @param paramTypes array of type descriptor strings - * @param endLabel jump label - * @param labels visit labels - * @param consArgsVar constructor args - * @param cv the code visitor to be used to generate the bytecode. */ - private void doSwitchBranch(int index, String targetClassName, String[] paramTypes, Label endLabel, - Label[] labels, int consArgsVar, MethodVisitor cv) { - cv.visitLabel(labels[index]); - - cv.visitVarInsn(ALOAD, 0); // push this before args - - // Unload the arguments from the ConstructorArgs object - for (String type : paramTypes) { - final String method; - if (type.equals("Z")) - method = "getBoolean"; - else if (type.equals("B")) - method = "getByte"; - else if (type.equals("C")) - method = "getChar"; - else if (type.equals("S")) - method = "getShort"; - else if (type.equals("I")) - method = "getInt"; - else if (type.equals("J")) - method = "getLong"; - else if (type.equals("D")) - method = "getDouble"; - else if (type.equals("F")) - method = "getFloat"; - else - method = "getObject"; - - // invoke the iterator method on the ConstructorArgs - cv.visitVarInsn(ALOAD, consArgsVar); // push the ConstructorArgs - String className = "bsh/This$ConstructorArgs"; - String retType; - if (method.equals("getObject")) - retType = OBJECT; - else - retType = type; - - cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()" + retType, false); - // if it's an object type we must do a check cast - if (method.equals("getObject")) - cv.visitTypeInsn(CHECKCAST, descriptorToClassName(type)); - } - - // invoke the constructor for this branch - String descriptor = getMethodDescriptor("V", paramTypes); - cv.visitMethodInsn(INVOKESPECIAL, targetClassName, "", descriptor, false); - cv.visitJumpInsn(GOTO, endLabel); - } - - private static String getMethodDescriptor(String returnType, String[] paramTypes) { - StringBuilder sb = new StringBuilder("("); - for (String paramType : paramTypes) - sb.append(paramType); - - sb.append(')').append(returnType); - return sb.toString(); - } - - /** - * Generate a superclass method delegate accessor method. - * These methods are specially named methods which allow access to - * overridden methods of the superclass (which the Java reflection API - * normally does not allow). - */ - // Maybe combine this with generateMethod() - private void generateSuperDelegateMethod(Class superClass, String superClassName, String methodName, String returnType, String[] paramTypes, int modifiers, ClassWriter cw) { - String[] exceptions = null; - - if (returnType == null) // map loose return to Object - returnType = OBJECT; - - String methodDescriptor = getMethodDescriptor(returnType, paramTypes); - - String paramTypesSig = getTypeParameterSignature(paramTypes); - - // Add method body - MethodVisitor cv = cw.visitMethod(modifiers, BSHSUPER + methodName, methodDescriptor, paramTypesSig, exceptions); - - cv.visitVarInsn(ALOAD, 0); - // Push vars - int localVarIndex = 1; - for (String paramType : paramTypes) { - if (isPrimitive(paramType)) - cv.visitVarInsn(ILOAD, localVarIndex); - else - cv.visitVarInsn(ALOAD, localVarIndex); - localVarIndex += paramType.equals("D") || paramType.equals("J") ? 2 : 1; - } - - cv.visitMethodInsn(INVOKESPECIAL, superClassName, methodName, methodDescriptor, false); - - generatePlainReturnCode(returnType, cv); - - // values here are ignored, computed automatically by ClassWriter - cv.visitMaxs(0, 0); - } - - /** - * Strict-Java validation using BeanShell's faux-reflection descriptors. - */ - static void checkAbstractMethodImplementation(Class type) { - BshMethod[] methods = Reflect.getDeclaredMethods(type); - for (int i = 0; i < methods.length; i++) { - BshMethod parent = methods[i]; - if (!parent.hasModifier("abstract")) { - continue; - } - - BshMethod impl = null; - for (int j = 0; j < methods.length; j++) { - BshMethod candidate = methods[j]; - if (candidate == parent) { - continue; - } - if (!candidate.getName().equals(parent.getName())) { - continue; - } - if (candidate.hasModifier("abstract") || candidate.hasModifier("private")) { - continue; - } - if (Types.areSignaturesEqual(parent.getParameterTypes(), candidate.getParameterTypes())) { - impl = candidate; - break; - } - } - - if (impl == null && !Reflect.getClassModifiers(type).hasModifier("abstract")) { - throw new RuntimeException(type.getName() - + " is not abstract and does not implement " - + parent.getName() + "()"); - } - } - } - - /** Generate return code for a normal bytecode - * @param returnType expect type descriptor string - * @param cv the code visitor to be used to generate the bytecode. */ - private static void generatePlainReturnCode(String returnType, MethodVisitor cv) { - if (returnType.equals("V")) - cv.visitInsn(RETURN); - else if (isPrimitive(returnType)) { - int opcode = IRETURN; - if (returnType.equals("D")) - opcode = DRETURN; - else if (returnType.equals("F")) - opcode = FRETURN; - else if (returnType.equals("J")) //long - opcode = LRETURN; - - cv.visitInsn(opcode); - } else { - cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); - cv.visitInsn(ARETURN); - } - } - - /** Generates the code to reify the arguments of the given method. - * For a method "int m (int i, String s)", this code is the bytecode - * corresponding to the "new Object[] { new bsh.Primitive(i), s }" - * expression. - * @author Eric Bruneton - * @author Pat Niemeyer - * @param cv the code visitor to be used to generate the bytecode. - * @param isStatic the enclosing methods is static */ - private void generateParameterReifierCode(String[] paramTypes, boolean isStatic, final MethodVisitor cv) { - cv.visitIntInsn(SIPUSH, paramTypes.length); - cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); - int localVarIndex = isStatic ? 0 : 1; - for (int i = 0; i < paramTypes.length; ++i) { - String param = paramTypes[i]; - cv.visitInsn(DUP); - cv.visitIntInsn(SIPUSH, i); - if (isPrimitive(param)) { - int opcode; - if (param.equals("F")) - opcode = FLOAD; - else if (param.equals("D")) - opcode = DLOAD; - else if (param.equals("J")) - opcode = LLOAD; - else - opcode = ILOAD; - - String type = "bsh/Primitive"; - cv.visitTypeInsn(NEW, type); - cv.visitInsn(DUP); - cv.visitVarInsn(opcode, localVarIndex); - cv.visitMethodInsn(INVOKESPECIAL, type, "", "(" + param + ")V", false); - cv.visitInsn(AASTORE); - } else { - // If null wrap value as bsh.Primitive.NULL. - cv.visitVarInsn(ALOAD, localVarIndex); - Label isnull = new Label(); - cv.visitJumpInsn(IFNONNULL, isnull); - cv.visitFieldInsn(GETSTATIC, "bsh/Primitive", "NULL", "Lbsh/Primitive;"); - cv.visitInsn(AASTORE); - // else store parameter as Object. - Label notnull = new Label(); - cv.visitJumpInsn(GOTO, notnull); - cv.visitLabel(isnull); - cv.visitVarInsn(ALOAD, localVarIndex); - cv.visitInsn(AASTORE); - cv.visitLabel(notnull); - } - localVarIndex += param.equals("D") || param.equals("J") ? 2 : 1; - } - } - - /** Generates the code to unreify the result of the given method. - * For a method "int m (int i, String s)", this code is the bytecode - * corresponding to the "((Integer)...).intValue()" expression. - * @author Eric Bruneton - * @author Pat Niemeyer - * @param returnType expect type descriptor string - * @param cv the code visitor to be used to generate the bytecode. */ - private void generateReturnCode(String returnType, MethodVisitor cv) { - if (returnType.equals("V")) { - cv.visitInsn(POP); - cv.visitInsn(RETURN); - } else if (isPrimitive(returnType)) { - int opcode = IRETURN; - String type; - String meth; - if (returnType.equals("Z")) { - type = "java/lang/Boolean"; - meth = "booleanValue"; - } else if (returnType.equals("C")) { - type = "java/lang/Character"; - meth = "charValue"; - } else if (returnType.equals("B")) { - type = "java/lang/Byte"; - meth = "byteValue"; - } else if (returnType.equals("S") ) { - type = "java/lang/Short"; - meth = "shortValue"; - } else if (returnType.equals("F")) { - opcode = FRETURN; - type = "java/lang/Float"; - meth = "floatValue"; - } else if (returnType.equals("J")) { - opcode = LRETURN; - type = "java/lang/Long"; - meth = "longValue"; - } else if (returnType.equals("D")) { - opcode = DRETURN; - type = "java/lang/Double"; - meth = "doubleValue"; - } else /*if (returnType.equals("I"))*/ { - type = "java/lang/Integer"; - meth = "intValue"; - } - - String desc = returnType; - cv.visitTypeInsn(CHECKCAST, type); // type is correct here - cv.visitMethodInsn(INVOKEVIRTUAL, type, meth, "()" + desc, false); - cv.visitInsn(opcode); - } else { - cv.visitTypeInsn(CHECKCAST, descriptorToClassName(returnType)); - cv.visitInsn(ARETURN); - } - } - - /** - * Does the type descriptor string describe a primitive type? - */ - private static boolean isPrimitive(String typeDescriptor) { - return typeDescriptor.length() == 1; // right? - } - - /** Returns type descriptors for the parameter types. - * @param cparams class list of parameter types - * @return String list of type descriptors */ - static String[] getTypeDescriptors(Class[] cparams) { - String[] sa = new String[cparams.length]; - for (int i = 0; i < sa.length; i++) - sa[i] = BSHType.getTypeDescriptor(cparams[i]); - return sa; - } - - /** If a non-array object type, remove the prefix "L" and suffix ";". - * @param s expect type descriptor string. - * @return class name */ - private static String descriptorToClassName(String s) { - if (s.startsWith("[") || !s.startsWith("L")) - return s; - return s.substring(1, s.length() - 1); - } - - /** - * Attempt to load a script named for the class: e.g. Foo.class Foo.bsh. - * The script is expected to (at minimum) initialize the class body. - * That is, it should contain the scripted class definition. - * - * This method relies on the fact that the ClassGenerator generateClass() - * method will detect that the generated class already exists and - * initialize it rather than recreating it. - * - * The only interact that this method has with the process is to initially - * cache the correct class in the class manager for the interpreter to - * insure that it is found and associated with the scripted body. - */ - public static void startInterpreterForClass(Class genClass) { - String fqClassName = genClass.getName(); - String baseName = Name.suffix(fqClassName, 1); - String resName = baseName + ".bsh"; - - URL url = genClass.getResource(resName); - if (null == url) - throw new InterpreterError("Script (" + resName + ") for BeanShell generated class: " + genClass + " not found."); - - // Set up the interpreter - try (Reader reader = new FileReader(genClass.getResourceAsStream(resName))) { - Interpreter bsh = new Interpreter(); - NameSpace globalNS = bsh.getNameSpace(); - globalNS.setName("class_" + baseName + "_global"); - globalNS.getClassManager().associateClass(genClass); +public final class ClassGeneratorUtil { + public static final int DEFAULTCONSTRUCTOR = -1; - // Source the script - bsh.eval(reader, globalNS, resName); - } catch (TargetError e) { - System.out.println("Script threw exception: " + e); - if (e.inNativeCode()) - e.printStackTrace(System.err); - } catch (IOException | EvalError e) { - System.out.println("Evaluation Error: " + e); - } + private ClassGeneratorUtil() { } } diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Attribute.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Attribute.java deleted file mode 100644 index b97be750d9..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Attribute.java +++ /dev/null @@ -1,290 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A non standard class, field, method or code attribute, as defined in the Java Virtual Machine - * Specification (JVMS). - * - * @see JVMS - * 4.7 - * @see JVMS - * 4.7.3 - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public class Attribute { - - /** The type of this attribute, also called its name in the JVMS. */ - public final String type; - - /** - * The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}). - * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are not - * included. - */ - private byte[] content; - - /** - * The next attribute in this attribute list (Attribute instances can be linked via this field to - * store a list of class, field, method or code attributes). May be null. - */ - Attribute nextAttribute; - - /** - * Constructs a new empty attribute. - * - * @param type the type of the attribute. - */ - protected Attribute(final String type) { - this.type = type; - } - - /** - * Returns true if this type of attribute is unknown. This means that the attribute - * content can't be parsed to extract constant pool references, labels, etc. Instead, the - * attribute content is read as an opaque byte array, and written back as is. This can lead to - * invalid attributes, if the content actually contains constant pool references, labels, or other - * symbolic references that need to be updated when there are changes to the constant pool, the - * method bytecode, etc. The default implementation of this method always returns true. - * - * @return true if this type of attribute is unknown. - */ - public boolean isUnknown() { - return true; - } - - /** - * Returns true if this type of attribute is a code attribute. - * - * @return true if this type of attribute is a code attribute. - */ - public boolean isCodeAttribute() { - return false; - } - - /** - * Returns the labels corresponding to this attribute. - * - * @return the labels corresponding to this attribute, or null if this attribute is not a - * code attribute that contains labels. - */ - protected Label[] getLabels() { - return new Label[0]; - } - - /** - * Returns the byte array form of the content of this attribute. The 6 header bytes - * (attribute_name_index and attribute_length) must not be added in the returned - * ByteVector. - * - * @param classWriter the class to which this attribute must be added. This parameter can be used - * to add the items that corresponds to this attribute to the constant pool of this class. - * @param code the bytecode of the method corresponding to this code attribute, or null - * if this attribute is not a code attribute. Corresponds to the 'code' field of the Code - * attribute. - * @param codeLength the length of the bytecode of the method corresponding to this code - * attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length' - * field of the Code attribute. - * @param maxStack the maximum stack size of the method corresponding to this code attribute, or - * -1 if this attribute is not a code attribute. - * @param maxLocals the maximum number of local variables of the method corresponding to this code - * attribute, or -1 if this attribute is not a code attribute. - * @return the byte array form of this attribute. - */ - protected ByteVector write( - final ClassWriter classWriter, - final byte[] code, - final int codeLength, - final int maxStack, - final int maxLocals) { - return new ByteVector(content); - } - - /** - * Returns the number of attributes of the attribute list that begins with this attribute. - * - * @return the number of attributes of the attribute list that begins with this attribute. - */ - final int getAttributeCount() { - int count = 0; - Attribute attribute = this; - while (attribute != null) { - count += 1; - attribute = attribute.nextAttribute; - } - return count; - } - - /** - * Returns the total size in bytes of all the attributes in the attribute list that begins with - * this attribute. This size includes the 6 header bytes (attribute_name_index and - * attribute_length) per attribute. Also adds the attribute type names to the constant pool. - * - * @param symbolTable where the constants used in the attributes must be stored. - * @return the size of all the attributes in this attribute list. This size includes the size of - * the attribute headers. - */ - final int computeAttributesSize(final SymbolTable symbolTable) { - final byte[] code = null; - final int codeLength = 0; - final int maxStack = -1; - final int maxLocals = -1; - return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals); - } - - /** - * Returns the total size in bytes of all the attributes in the attribute list that begins with - * this attribute. This size includes the 6 header bytes (attribute_name_index and - * attribute_length) per attribute. Also adds the attribute type names to the constant pool. - * - * @param symbolTable where the constants used in the attributes must be stored. - * @param code the bytecode of the method corresponding to these code attributes, or null - * if they are not code attributes. Corresponds to the 'code' field of the Code attribute. - * @param codeLength the length of the bytecode of the method corresponding to these code - * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of - * the Code attribute. - * @param maxStack the maximum stack size of the method corresponding to these code attributes, or - * -1 if they are not code attributes. - * @param maxLocals the maximum number of local variables of the method corresponding to these - * code attributes, or -1 if they are not code attribute. - * @return the size of all the attributes in this attribute list. This size includes the size of - * the attribute headers. - */ - final int computeAttributesSize( - final SymbolTable symbolTable, - final byte[] code, - final int codeLength, - final int maxStack, - final int maxLocals) { - final ClassWriter classWriter = symbolTable.classWriter; - int size = 0; - Attribute attribute = this; - while (attribute != null) { - symbolTable.addConstantUtf8(attribute.type); - size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length; - attribute = attribute.nextAttribute; - } - return size; - } - - /** - * Puts all the attributes of the attribute list that begins with this attribute, in the given - * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per - * attribute. - * - * @param symbolTable where the constants used in the attributes must be stored. - * @param output where the attributes must be written. - */ - final void putAttributes(final SymbolTable symbolTable, final ByteVector output) { - final byte[] code = null; - final int codeLength = 0; - final int maxStack = -1; - final int maxLocals = -1; - putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output); - } - - /** - * Puts all the attributes of the attribute list that begins with this attribute, in the given - * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per - * attribute. - * - * @param symbolTable where the constants used in the attributes must be stored. - * @param code the bytecode of the method corresponding to these code attributes, or null - * if they are not code attributes. Corresponds to the 'code' field of the Code attribute. - * @param codeLength the length of the bytecode of the method corresponding to these code - * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of - * the Code attribute. - * @param maxStack the maximum stack size of the method corresponding to these code attributes, or - * -1 if they are not code attributes. - * @param maxLocals the maximum number of local variables of the method corresponding to these - * code attributes, or -1 if they are not code attribute. - * @param output where the attributes must be written. - */ - final void putAttributes( - final SymbolTable symbolTable, - final byte[] code, - final int codeLength, - final int maxStack, - final int maxLocals, - final ByteVector output) { - final ClassWriter classWriter = symbolTable.classWriter; - Attribute attribute = this; - while (attribute != null) { - ByteVector attributeContent = - attribute.write(classWriter, code, codeLength, maxStack, maxLocals); - // Put attribute_name_index and attribute_length. - output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length); - output.putByteArray(attributeContent.data, 0, attributeContent.length); - attribute = attribute.nextAttribute; - } - } - - /** A set of attribute prototypes (attributes with the same type are considered equal). */ - static final class Set { - - private static final int SIZE_INCREMENT = 6; - - private int size; - private Attribute[] data = new Attribute[SIZE_INCREMENT]; - - void addAttributes(final Attribute attributeList) { - Attribute attribute = attributeList; - while (attribute != null) { - if (!contains(attribute)) { - add(attribute); - } - attribute = attribute.nextAttribute; - } - } - - Attribute[] toArray() { - Attribute[] result = new Attribute[size]; - System.arraycopy(data, 0, result, 0, size); - return result; - } - - private boolean contains(final Attribute attribute) { - for (int i = 0; i < size; ++i) { - if (data[i].type.equals(attribute.type)) { - return true; - } - } - return false; - } - - private void add(final Attribute attribute) { - if (size >= data.length) { - Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT]; - System.arraycopy(data, 0, newData, 0, size); - data = newData; - } - data[size++] = attribute; - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ByteVector.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ByteVector.java deleted file mode 100644 index 2c1567d229..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ByteVector.java +++ /dev/null @@ -1,360 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream - * on top of a ByteArrayOutputStream, but is more efficient. - * - * @author Eric Bruneton - */ -public class ByteVector { - - /** The content of this vector. Only the first {@link #length} bytes contain real data. */ - byte[] data; - - /** The actual number of bytes in this vector. */ - int length; - - /** Constructs a new {@link ByteVector} with a default initial capacity. */ - public ByteVector() { - data = new byte[64]; - } - - /** - * Constructs a new {@link ByteVector} with the given initial capacity. - * - * @param initialCapacity the initial capacity of the byte vector to be constructed. - */ - public ByteVector(final int initialCapacity) { - data = new byte[initialCapacity]; - } - - /** - * Constructs a new {@link ByteVector} from the given initial data. - * - * @param data the initial data of the new byte vector. - */ - ByteVector(final byte[] data) { - this.data = data; - this.length = data.length; - } - - /** - * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary. - * - * @param byteValue a byte. - * @return this byte vector. - */ - public ByteVector putByte(final int byteValue) { - int currentLength = length; - if (currentLength + 1 > data.length) { - enlarge(1); - } - data[currentLength++] = (byte) byteValue; - length = currentLength; - return this; - } - - /** - * Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary. - * - * @param byteValue1 a byte. - * @param byteValue2 another byte. - * @return this byte vector. - */ - final ByteVector put11(final int byteValue1, final int byteValue2) { - int currentLength = length; - if (currentLength + 2 > data.length) { - enlarge(2); - } - byte[] currentData = data; - currentData[currentLength++] = (byte) byteValue1; - currentData[currentLength++] = (byte) byteValue2; - length = currentLength; - return this; - } - - /** - * Puts a short into this byte vector. The byte vector is automatically enlarged if necessary. - * - * @param shortValue a short. - * @return this byte vector. - */ - public ByteVector putShort(final int shortValue) { - int currentLength = length; - if (currentLength + 2 > data.length) { - enlarge(2); - } - byte[] currentData = data; - currentData[currentLength++] = (byte) (shortValue >>> 8); - currentData[currentLength++] = (byte) shortValue; - length = currentLength; - return this; - } - - /** - * Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if - * necessary. - * - * @param byteValue a byte. - * @param shortValue a short. - * @return this byte vector. - */ - final ByteVector put12(final int byteValue, final int shortValue) { - int currentLength = length; - if (currentLength + 3 > data.length) { - enlarge(3); - } - byte[] currentData = data; - currentData[currentLength++] = (byte) byteValue; - currentData[currentLength++] = (byte) (shortValue >>> 8); - currentData[currentLength++] = (byte) shortValue; - length = currentLength; - return this; - } - - /** - * Puts two bytes and a short into this byte vector. The byte vector is automatically enlarged if - * necessary. - * - * @param byteValue1 a byte. - * @param byteValue2 another byte. - * @param shortValue a short. - * @return this byte vector. - */ - final ByteVector put112(final int byteValue1, final int byteValue2, final int shortValue) { - int currentLength = length; - if (currentLength + 4 > data.length) { - enlarge(4); - } - byte[] currentData = data; - currentData[currentLength++] = (byte) byteValue1; - currentData[currentLength++] = (byte) byteValue2; - currentData[currentLength++] = (byte) (shortValue >>> 8); - currentData[currentLength++] = (byte) shortValue; - length = currentLength; - return this; - } - - /** - * Puts an int into this byte vector. The byte vector is automatically enlarged if necessary. - * - * @param intValue an int. - * @return this byte vector. - */ - public ByteVector putInt(final int intValue) { - int currentLength = length; - if (currentLength + 4 > data.length) { - enlarge(4); - } - byte[] currentData = data; - currentData[currentLength++] = (byte) (intValue >>> 24); - currentData[currentLength++] = (byte) (intValue >>> 16); - currentData[currentLength++] = (byte) (intValue >>> 8); - currentData[currentLength++] = (byte) intValue; - length = currentLength; - return this; - } - - /** - * Puts one byte and two shorts into this byte vector. The byte vector is automatically enlarged - * if necessary. - * - * @param byteValue a byte. - * @param shortValue1 a short. - * @param shortValue2 another short. - * @return this byte vector. - */ - final ByteVector put122(final int byteValue, final int shortValue1, final int shortValue2) { - int currentLength = length; - if (currentLength + 5 > data.length) { - enlarge(5); - } - byte[] currentData = data; - currentData[currentLength++] = (byte) byteValue; - currentData[currentLength++] = (byte) (shortValue1 >>> 8); - currentData[currentLength++] = (byte) shortValue1; - currentData[currentLength++] = (byte) (shortValue2 >>> 8); - currentData[currentLength++] = (byte) shortValue2; - length = currentLength; - return this; - } - - /** - * Puts a long into this byte vector. The byte vector is automatically enlarged if necessary. - * - * @param longValue a long. - * @return this byte vector. - */ - public ByteVector putLong(final long longValue) { - int currentLength = length; - if (currentLength + 8 > data.length) { - enlarge(8); - } - byte[] currentData = data; - int intValue = (int) (longValue >>> 32); - currentData[currentLength++] = (byte) (intValue >>> 24); - currentData[currentLength++] = (byte) (intValue >>> 16); - currentData[currentLength++] = (byte) (intValue >>> 8); - currentData[currentLength++] = (byte) intValue; - intValue = (int) longValue; - currentData[currentLength++] = (byte) (intValue >>> 24); - currentData[currentLength++] = (byte) (intValue >>> 16); - currentData[currentLength++] = (byte) (intValue >>> 8); - currentData[currentLength++] = (byte) intValue; - length = currentLength; - return this; - } - - /** - * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if - * necessary. - * - * @param stringValue a String whose UTF8 encoded length must be less than 65536. - * @return this byte vector. - */ - public ByteVector putUTF8(final String stringValue) { - int charLength = stringValue.length(); - if (charLength > 65535) { - throw new IllegalArgumentException(); - } - int currentLength = length; - if (currentLength + 2 + charLength > data.length) { - enlarge(2 + charLength); - } - byte[] currentData = data; - // Optimistic algorithm: instead of computing the byte length and then serializing the string - // (which requires two loops), we assume the byte length is equal to char length (which is the - // most frequent case), and we start serializing the string right away. During the - // serialization, if we find that this assumption is wrong, we continue with the general method. - currentData[currentLength++] = (byte) (charLength >>> 8); - currentData[currentLength++] = (byte) charLength; - for (int i = 0; i < charLength; ++i) { - char charValue = stringValue.charAt(i); - if (charValue >= '\u0001' && charValue <= '\u007F') { - currentData[currentLength++] = (byte) charValue; - } else { - length = currentLength; - return encodeUTF8(stringValue, i, 65535); - } - } - length = currentLength; - return this; - } - - /** - * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if - * necessary. The string length is encoded in two bytes before the encoded characters, if there is - * space for that (i.e. if this.length - offset - 2 >= 0). - * - * @param stringValue the String to encode. - * @param offset the index of the first character to encode. The previous characters are supposed - * to have already been encoded, using only one byte per character. - * @param maxByteLength the maximum byte length of the encoded string, including the already - * encoded characters. - * @return this byte vector. - */ - final ByteVector encodeUTF8(final String stringValue, final int offset, final int maxByteLength) { - int charLength = stringValue.length(); - int byteLength = offset; - for (int i = offset; i < charLength; ++i) { - char charValue = stringValue.charAt(i); - if (charValue >= '\u0001' && charValue <= '\u007F') { - byteLength++; - } else if (charValue <= '\u07FF') { - byteLength += 2; - } else { - byteLength += 3; - } - } - if (byteLength > maxByteLength) { - throw new IllegalArgumentException(); - } - // Compute where 'byteLength' must be stored in 'data', and store it at this location. - int byteLengthOffset = length - offset - 2; - if (byteLengthOffset >= 0) { - data[byteLengthOffset] = (byte) (byteLength >>> 8); - data[byteLengthOffset + 1] = (byte) byteLength; - } - if (length + byteLength - offset > data.length) { - enlarge(byteLength - offset); - } - int currentLength = length; - for (int i = offset; i < charLength; ++i) { - char charValue = stringValue.charAt(i); - if (charValue >= '\u0001' && charValue <= '\u007F') { - data[currentLength++] = (byte) charValue; - } else if (charValue <= '\u07FF') { - data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F); - data[currentLength++] = (byte) (0x80 | charValue & 0x3F); - } else { - data[currentLength++] = (byte) (0xE0 | charValue >> 12 & 0xF); - data[currentLength++] = (byte) (0x80 | charValue >> 6 & 0x3F); - data[currentLength++] = (byte) (0x80 | charValue & 0x3F); - } - } - length = currentLength; - return this; - } - - /** - * Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if - * necessary. - * - * @param byteArrayValue an array of bytes. May be null to put byteLength null - * bytes into this byte vector. - * @param byteOffset index of the first byte of byteArrayValue that must be copied. - * @param byteLength number of bytes of byteArrayValue that must be copied. - * @return this byte vector. - */ - public ByteVector putByteArray( - final byte[] byteArrayValue, final int byteOffset, final int byteLength) { - if (length + byteLength > data.length) { - enlarge(byteLength); - } - if (byteArrayValue != null) { - System.arraycopy(byteArrayValue, byteOffset, data, length, byteLength); - } - length += byteLength; - return this; - } - - /** - * Enlarges this byte vector so that it can receive 'size' more bytes. - * - * @param size number of additional bytes that this byte vector should be able to receive. - */ - private void enlarge(final int size) { - int doubleCapacity = 2 * data.length; - int minimalCapacity = length + size; - byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity]; - System.arraycopy(data, 0, newData, 0, length); - data = newData; - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassVisitor.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassVisitor.java deleted file mode 100644 index 7b1000bfbe..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassVisitor.java +++ /dev/null @@ -1,235 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A visitor to visit a Java class. The methods of this class must be called in the following order: - * visit [ visitSource ] [ visitModule ][ visitNestHost ][ - * visitOuterClass ] ( visitAnnotation | visitTypeAnnotation | - * visitAttribute )* ( visitNestMember | visitInnerClass | - * visitField | visitMethod )* visitEnd. - * - * @author Eric Bruneton - */ -public abstract class ClassVisitor { - - /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7_EXPERIMENTAL}. - */ - protected final int api; - - /** The class visitor to which this visitor must delegate method calls. May be null. */ - protected ClassVisitor cv; - - /** - * Constructs a new {@link ClassVisitor}. - * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link - * Opcodes#ASM7_EXPERIMENTAL}. - */ - public ClassVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link ClassVisitor}. - * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link - * Opcodes#ASM7_EXPERIMENTAL}. - * @param classVisitor the class visitor to which this visitor must delegate method calls. May be - * null. - */ - public ClassVisitor(final int api, final ClassVisitor classVisitor) { - if (api != Opcodes.ASM6 - && api != Opcodes.ASM5 - && api != Opcodes.ASM4) { - throw new IllegalArgumentException(); - } - this.api = api; - this.cv = classVisitor; - } - - /** - * Visits the header of the class. - * - * @param version the class version. The minor version is stored in the 16 most significant bits, - * and the major version in the 16 least significant bits. - * @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if - * the class is deprecated. - * @param name the internal name of the class (see {@link Type#getInternalName()}). - * @param signature the signature of this class. May be null if the class is not a - * generic one, and does not extend or implement generic classes or interfaces. - * @param superName the internal of name of the super class (see {@link Type#getInternalName()}). - * For interfaces, the super class is {@link Object}. May be null, but only for the - * {@link Object} class. - * @param interfaces the internal names of the class's interfaces (see {@link - * Type#getInternalName()}). May be null. - */ - public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces) { - if (cv != null) { - cv.visit(version, access, name, signature, superName, interfaces); - } - } - - /** - * Visits the source of the class. - * - * @param source the name of the source file from which the class was compiled. May be - * null. - * @param debug additional debug information to compute the correspondence between source and - * compiled elements of the class. May be null. - */ - public void visitSource(final String source, final String debug) { - if (cv != null) { - cv.visitSource(source, debug); - } - } - - /** - * Visits the enclosing class of the class. This method must be called only if the class has an - * enclosing class. - * - * @param owner internal name of the enclosing class of the class. - * @param name the name of the method that contains the class, or null if the class is - * not enclosed in a method of its enclosing class. - * @param descriptor the descriptor of the method that contains the class, or null if the - * class is not enclosed in a method of its enclosing class. - */ - public void visitOuterClass(final String owner, final String name, final String descriptor) { - if (cv != null) { - cv.visitOuterClass(owner, name, descriptor); - } - } - - /** - * Visits a non standard attribute of the class. - * - * @param attribute an attribute. - */ - public void visitAttribute(final Attribute attribute) { - if (cv != null) { - cv.visitAttribute(attribute); - } - } - - /** - * Visits information about an inner class. This inner class is not necessarily a member of the - * class being visited. - * - * @param name the internal name of an inner class (see {@link Type#getInternalName()}). - * @param outerName the internal name of the class to which the inner class belongs (see {@link - * Type#getInternalName()}). May be null for not member classes. - * @param innerName the (simple) name of the inner class inside its enclosing class. May be - * null for anonymous inner classes. - * @param access the access flags of the inner class as originally declared in the enclosing - * class. - */ - public void visitInnerClass( - final String name, final String outerName, final String innerName, final int access) { - if (cv != null) { - cv.visitInnerClass(name, outerName, innerName, access); - } - } - - /** - * Visits a field of the class. - * - * @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if - * the field is synthetic and/or deprecated. - * @param name the field's name. - * @param descriptor the field's descriptor (see {@link Type}). - * @param signature the field's signature. May be null if the field's type does not use - * generic types. - * @param value the field's initial value. This parameter, which may be null if the field - * does not have an initial value, must be an {@link Integer}, a {@link Float}, a {@link - * Long}, a {@link Double} or a {@link String} (for int, float, - * long or String fields respectively). This parameter is only used for - * static fields. Its value is ignored for non static fields, which must be initialized - * through bytecode instructions in constructors or methods. - * @return a visitor to visit field annotations and attributes, or null if this class - * visitor is not interested in visiting these annotations and attributes. - */ - public FieldVisitor visitField( - final int access, - final String name, - final String descriptor, - final String signature, - final Object value) { - if (cv != null) { - return cv.visitField(access, name, descriptor, signature, value); - } - return null; - } - - /** - * Visits a method of the class. This method must return a new {@link MethodVisitor} - * instance (or null) each time it is called, i.e., it should not return a previously - * returned visitor. - * - * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if - * the method is synthetic and/or deprecated. - * @param name the method's name. - * @param descriptor the method's descriptor (see {@link Type}). - * @param signature the method's signature. May be null if the method parameters, return - * type and exceptions do not use generic types. - * @param exceptions the internal names of the method's exception classes (see {@link - * Type#getInternalName()}). May be null. - * @return an object to visit the byte code of the method, or null if this class visitor - * is not interested in visiting the code of this method. - */ - public MethodVisitor visitMethod( - final int access, - final String name, - final String descriptor, - final String signature, - final String[] exceptions) { - if (cv != null) { - return cv.visitMethod(access, name, descriptor, signature, exceptions); - } - return null; - } - - /** - * Visits the end of the class. This method, which is the last one to be called, is used to inform - * the visitor that all the fields and methods of the class have been visited. - */ - public void visitEnd() { - if (cv != null) { - cv.visitEnd(); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassWriter.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassWriter.java deleted file mode 100644 index 95f6d78c1a..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/ClassWriter.java +++ /dev/null @@ -1,757 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A {@link ClassVisitor} that generates a corresponding ClassFile structure, as defined in the Java - * Virtual Machine Specification (JVMS). It can be used alone, to generate a Java class "from - * scratch", or with one or more {@link ClassReader} and adapter {@link ClassVisitor} to generate a - * modified class from one or more existing Java classes. - * - * @see JVMS 4 - * @author Eric Bruneton - */ -public class ClassWriter extends ClassVisitor { - - /** - * A flag to automatically compute the maximum stack size and the maximum number of local - * variables of methods. If this flag is set, then the arguments of the {@link - * MethodVisitor#visitMaxs} method of the {@link MethodVisitor} returned by the {@link - * #visitMethod} method will be ignored, and computed automatically from the signature and the - * bytecode of each method. - * - *

Note: for classes whose version is {@link Opcodes#V1_7} of more, this option requires - * valid stack map frames. The maximum stack size is then computed from these frames, and from the - * bytecode instructions in between. If stack map frames are not present or must be recomputed, - * used {@link #COMPUTE_FRAMES} instead. - * - * @see #ClassWriter(int) - */ - public static final int COMPUTE_MAXS = 1; - - /** - * A flag to automatically compute the stack map frames of methods from scratch. If this flag is - * set, then the calls to the {@link MethodVisitor#visitFrame} method are ignored, and the stack - * map frames are recomputed from the methods bytecode. The arguments of the {@link - * MethodVisitor#visitMaxs} method are also ignored and recomputed from the bytecode. In other - * words, {@link #COMPUTE_FRAMES} implies {@link #COMPUTE_MAXS}. - * - * @see #ClassWriter(int) - */ - public static final int COMPUTE_FRAMES = 2; - - // Note: fields are ordered as in the ClassFile structure, and those related to attributes are - // ordered as in Section 4.7 of the JVMS. - - /** - * The minor_version and major_version fields of the JVMS ClassFile structure. minor_version is - * stored in the 16 most significant bits, and major_version in the 16 least significant bits. - */ - private int version; - - /** The symbol table for this class (contains the constant_pool and the BootstrapMethods). */ - private final SymbolTable symbolTable; - - /** - * The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific - * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the - * ClassFile structure. - */ - private int accessFlags; - - /** The this_class field of the JVMS ClassFile structure. */ - private int thisClass; - - /** The super_class field of the JVMS ClassFile structure. */ - private int superClass; - - /** The interface_count field of the JVMS ClassFile structure. */ - private int interfaceCount; - - /** The 'interfaces' array of the JVMS ClassFile structure. */ - private int[] interfaces; - - /** - * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their - * {@link FieldWriter#fv} field. This field stores the first element of this list. - */ - private FieldWriter firstField; - - /** - * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their - * {@link FieldWriter#fv} field. This field stores the last element of this list. - */ - private FieldWriter lastField; - - /** - * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their - * {@link MethodWriter#mv} field. This field stores the first element of this list. - */ - private MethodWriter firstMethod; - - /** - * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their - * {@link MethodWriter#mv} field. This field stores the last element of this list. - */ - private MethodWriter lastMethod; - - /** The number_of_classes field of the InnerClasses attribute, or 0. */ - private int numberOfInnerClasses; - - /** The 'classes' array of the InnerClasses attribute, or null. */ - private ByteVector innerClasses; - - /** The class_index field of the EnclosingMethod attribute, or 0. */ - private int enclosingClassIndex; - - /** The method_index field of the EnclosingMethod attribute. */ - private int enclosingMethodIndex; - - /** The signature_index field of the Signature attribute, or 0. */ - private int signatureIndex; - - /** The source_file_index field of the SourceFile attribute, or 0. */ - private int sourceFileIndex; - - /** The debug_extension field of the SourceDebugExtension attribute, or null. */ - private ByteVector debugExtension; - - /** - * The first non standard attribute of this class. The next ones can be accessed with the {@link - * Attribute#nextAttribute} field. May be null. - * - *

WARNING: this list stores the attributes in the reverse order of their visit. - * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link - * #toByteArray} method writes the attributes in the order defined by this list, i.e. in the - * reverse order specified by the user. - */ - private Attribute firstAttribute; - - /** - * Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link - * MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link - * MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}. - */ - private int compute; - - // ----------------------------------------------------------------------------------------------- - // Constructor - // ----------------------------------------------------------------------------------------------- - - /** - * Constructs a new {@link ClassWriter} object and enables optimizations for "mostly add" bytecode - * transformations. These optimizations are the following: - * - *

    - *
  • The constant pool and bootstrap methods from the original class are copied as is in the - * new class, which saves time. New constant pool entries and new bootstrap methods will be - * added at the end if necessary, but unused constant pool entries or bootstrap methods - * won't be removed. - *
  • Methods that are not transformed are copied as is in the new class, directly from the - * original class bytecode (i.e. without emitting visit events for all the method - * instructions), which saves a lot of time. Untransformed methods are detected by - * the fact that the {@link ClassReader} receives {@link MethodVisitor} objects that come - * from a {@link ClassWriter} (and not from any other {@link ClassVisitor} instance). - *
- * - * @param flags option flags that can be used to modify the default behavior of this class.Must be - * zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. These option flags do - * not affect methods that are copied as is in the new class. This means that neither the - * maximum stack size nor the stack frames will be computed for these methods. - */ - public ClassWriter(final int flags) { - super(Opcodes.ASM6); - symbolTable = new SymbolTable(this); - if ((flags & COMPUTE_FRAMES) != 0) { - this.compute = MethodWriter.COMPUTE_ALL_FRAMES; - } else if ((flags & COMPUTE_MAXS) != 0) { - this.compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL; - } else { - this.compute = MethodWriter.COMPUTE_NOTHING; - } - } - - // ----------------------------------------------------------------------------------------------- - // Implementation of the ClassVisitor abstract class - // ----------------------------------------------------------------------------------------------- - - @Override - public final void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces) { - this.version = version; - this.accessFlags = access; - this.thisClass = symbolTable.setMajorVersionAndClassName(version & 0xFFFF, name); - if (signature != null) { - this.signatureIndex = symbolTable.addConstantUtf8(signature); - } - this.superClass = superName == null ? 0 : symbolTable.addConstantClass(superName).index; - if (interfaces != null && interfaces.length > 0) { - interfaceCount = interfaces.length; - this.interfaces = new int[interfaceCount]; - for (int i = 0; i < interfaceCount; ++i) { - this.interfaces[i] = symbolTable.addConstantClass(interfaces[i]).index; - } - } - if (compute == MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL && (version & 0xFFFF) >= Opcodes.V1_7) { - compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES; - } - } - - @Override - public final void visitSource(final String file, final String debug) { - if (file != null) { - sourceFileIndex = symbolTable.addConstantUtf8(file); - } - if (debug != null) { - debugExtension = new ByteVector().encodeUTF8(debug, 0, Integer.MAX_VALUE); - } - } - - @Override - public final void visitOuterClass( - final String owner, final String name, final String descriptor) { - enclosingClassIndex = symbolTable.addConstantClass(owner).index; - if (name != null && descriptor != null) { - enclosingMethodIndex = symbolTable.addConstantNameAndType(name, descriptor); - } - } - - @Override - public final void visitAttribute(final Attribute attribute) { - // Store the attributes in the reverse order of their visit by this method. - attribute.nextAttribute = firstAttribute; - firstAttribute = attribute; - } - - @Override - public final void visitInnerClass( - final String name, final String outerName, final String innerName, final int access) { - if (innerClasses == null) { - innerClasses = new ByteVector(); - } - // Section 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the constant_pool table - // which represents a class or interface C that is not a package member must have exactly one - // corresponding entry in the classes array". To avoid duplicates we keep track in the info - // field of the Symbol of each CONSTANT_Class_info entry C whether an inner class entry has - // already been added for C. If so, we store the index of this inner class entry (plus one) in - // the info field. This trick allows duplicate detection in O(1) time. - Symbol nameSymbol = symbolTable.addConstantClass(name); - if (nameSymbol.info == 0) { - ++numberOfInnerClasses; - innerClasses.putShort(nameSymbol.index); - innerClasses.putShort(outerName == null ? 0 : symbolTable.addConstantClass(outerName).index); - innerClasses.putShort(innerName == null ? 0 : symbolTable.addConstantUtf8(innerName)); - innerClasses.putShort(access); - nameSymbol.info = numberOfInnerClasses; - } else { - // Compare the inner classes entry nameSymbol.info - 1 with the arguments of this method and - // throw an exception if there is a difference? - } - } - - @Override - public final FieldVisitor visitField( - final int access, - final String name, - final String descriptor, - final String signature, - final Object value) { - FieldWriter fieldWriter = - new FieldWriter(symbolTable, access, name, descriptor, signature, value); - if (firstField == null) { - firstField = fieldWriter; - } else { - lastField.fv = fieldWriter; - } - return lastField = fieldWriter; - } - - @Override - public final MethodVisitor visitMethod( - final int access, - final String name, - final String descriptor, - final String signature, - final String[] exceptions) { - MethodWriter methodWriter = - new MethodWriter(symbolTable, access, name, descriptor, signature, exceptions, compute); - if (firstMethod == null) { - firstMethod = methodWriter; - } else { - lastMethod.mv = methodWriter; - } - return lastMethod = methodWriter; - } - - @Override - public final void visitEnd() { - // Nothing to do. - } - - // ----------------------------------------------------------------------------------------------- - // Other public methods - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the content of the class file that was built by this ClassWriter. - * - * @return the binary content of the JVMS ClassFile structure that was built by this ClassWriter. - */ - public byte[] toByteArray() { - // First step: compute the size in bytes of the ClassFile structure. - // The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version, - // constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count, - // methods_count and attributes_count) use 2 bytes each, and each interface uses 2 bytes too. - int size = 24 + 2 * interfaceCount; - int fieldsCount = 0; - FieldWriter fieldWriter = firstField; - while (fieldWriter != null) { - ++fieldsCount; - size += fieldWriter.computeFieldInfoSize(); - fieldWriter = (FieldWriter) fieldWriter.fv; - } - int methodsCount = 0; - MethodWriter methodWriter = firstMethod; - while (methodWriter != null) { - ++methodsCount; - size += methodWriter.computeMethodInfoSize(); - methodWriter = (MethodWriter) methodWriter.mv; - } - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - int attributesCount = 0; - if (innerClasses != null) { - ++attributesCount; - size += 8 + innerClasses.length; - symbolTable.addConstantUtf8(Constants.INNER_CLASSES); - } - if (enclosingClassIndex != 0) { - ++attributesCount; - size += 10; - symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD); - } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) { - ++attributesCount; - size += 6; - symbolTable.addConstantUtf8(Constants.SYNTHETIC); - } - if (signatureIndex != 0) { - ++attributesCount; - size += 8; - symbolTable.addConstantUtf8(Constants.SIGNATURE); - } - if (sourceFileIndex != 0) { - ++attributesCount; - size += 8; - symbolTable.addConstantUtf8(Constants.SOURCE_FILE); - } - if (debugExtension != null) { - ++attributesCount; - size += 6 + debugExtension.length; - symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION); - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - ++attributesCount; - size += 6; - symbolTable.addConstantUtf8(Constants.DEPRECATED); - } - if (symbolTable.computeBootstrapMethodsSize() > 0) { - ++attributesCount; - size += symbolTable.computeBootstrapMethodsSize(); - } - if (firstAttribute != null) { - attributesCount += firstAttribute.getAttributeCount(); - size += firstAttribute.computeAttributesSize(symbolTable); - } - // IMPORTANT: this must be the last part of the ClassFile size computation, because the previous - // statements can add attribute names to the constant pool, thereby changing its size! - size += symbolTable.getConstantPoolLength(); - if (symbolTable.getConstantPoolCount() > 0xFFFF) { - throw new IndexOutOfBoundsException("Class file too large!"); - } - - // Second step: allocate a ByteVector of the correct size (in order to avoid any array copy in - // dynamic resizes) and fill it with the ClassFile content. - ByteVector result = new ByteVector(size); - result.putInt(0xCAFEBABE).putInt(version); - symbolTable.putConstantPool(result); - int mask = (version & 0xFFFF) < Opcodes.V1_5 ? Opcodes.ACC_SYNTHETIC : 0; - result.putShort(accessFlags & ~mask).putShort(thisClass).putShort(superClass); - result.putShort(interfaceCount); - for (int i = 0; i < interfaceCount; ++i) { - result.putShort(interfaces[i]); - } - result.putShort(fieldsCount); - fieldWriter = firstField; - while (fieldWriter != null) { - fieldWriter.putFieldInfo(result); - fieldWriter = (FieldWriter) fieldWriter.fv; - } - result.putShort(methodsCount); - boolean hasFrames = false; - boolean hasAsmInstructions = false; - methodWriter = firstMethod; - while (methodWriter != null) { - hasFrames |= methodWriter.hasFrames(); - hasAsmInstructions |= methodWriter.hasAsmInstructions(); - methodWriter.putMethodInfo(result); - methodWriter = (MethodWriter) methodWriter.mv; - } - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - result.putShort(attributesCount); - if (innerClasses != null) { - result - .putShort(symbolTable.addConstantUtf8(Constants.INNER_CLASSES)) - .putInt(innerClasses.length + 2) - .putShort(numberOfInnerClasses) - .putByteArray(innerClasses.data, 0, innerClasses.length); - } - if (enclosingClassIndex != 0) { - result - .putShort(symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD)) - .putInt(4) - .putShort(enclosingClassIndex) - .putShort(enclosingMethodIndex); - } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) { - result.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); - } - if (signatureIndex != 0) { - result - .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) - .putInt(2) - .putShort(signatureIndex); - } - if (sourceFileIndex != 0) { - result - .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_FILE)) - .putInt(2) - .putShort(sourceFileIndex); - } - if (debugExtension != null) { - int length = debugExtension.length; - result - .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION)) - .putInt(length) - .putByteArray(debugExtension.data, 0, length); - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); - } - symbolTable.putBootstrapMethods(result); - if (firstAttribute != null) { - firstAttribute.putAttributes(symbolTable, result); - } - - // Third step: replace the ASM specific instructions, if any. - if (hasAsmInstructions) { - return replaceAsmInstructions(result.data, hasFrames); - } else { - return result.data; - } - } - - /** - * Returns the equivalent of the given class file, with the ASM specific instructions replaced - * with standard ones. This is done with a ClassReader -> ClassWriter round trip. - * - * @param classFile a class file containing ASM specific instructions, generated by this - * ClassWriter. - * @param hasFrames whether there is at least one stack map frames in 'classFile'. - * @return an equivalent of 'classFile', with the ASM specific instructions replaced with standard - * ones. - */ - private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) { - Attribute[] attributes = getAttributePrototypes(); - firstField = null; - lastField = null; - firstMethod = null; - lastMethod = null; - firstAttribute = null; - compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING; - return toByteArray(); - } - - /** - * Returns the prototypes of the attributes used by this class, its fields and its methods. - * - * @return the prototypes of the attributes used by this class, its fields and its methods. - */ - private Attribute[] getAttributePrototypes() { - Attribute.Set attributePrototypes = new Attribute.Set(); - attributePrototypes.addAttributes(firstAttribute); - FieldWriter fieldWriter = firstField; - while (fieldWriter != null) { - fieldWriter.collectAttributePrototypes(attributePrototypes); - fieldWriter = (FieldWriter) fieldWriter.fv; - } - MethodWriter methodWriter = firstMethod; - while (methodWriter != null) { - methodWriter.collectAttributePrototypes(attributePrototypes); - methodWriter = (MethodWriter) methodWriter.mv; - } - return attributePrototypes.toArray(); - } - - // ----------------------------------------------------------------------------------------------- - // Utility methods: constant pool management for Attribute sub classes - // ----------------------------------------------------------------------------------------------- - - /** - * Adds a number or string constant to the constant pool of the class being build. Does nothing if - * the constant pool already contains a similar item. This method is intended for {@link - * Attribute} sub classes, and is normally not needed by class generators or adapters. - * - * @param value the value of the constant to be added to the constant pool. This parameter must be - * an {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double} or a {@link String}. - * @return the index of a new or already existing constant item with the given value. - */ - public int newConst(final Object value) { - return symbolTable.addConstant(value).index; - } - - /** - * Adds an UTF8 string to the constant pool of the class being build. Does nothing if the constant - * pool already contains a similar item. This method is intended for {@link Attribute} sub - * classes, and is normally not needed by class generators or adapters. - * - * @param value the String value. - * @return the index of a new or already existing UTF8 item. - */ - public int newUTF8(final String value) { - return symbolTable.addConstantUtf8(value); - } - - /** - * Adds a class reference to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param value the internal name of the class. - * @return the index of a new or already existing class reference item. - */ - public int newClass(final String value) { - return symbolTable.addConstantClass(value).index; - } - - /** - * Adds a method type reference to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param methodDescriptor method descriptor of the method type. - * @return the index of a new or already existing method type reference item. - */ - public int newMethodType(final String methodDescriptor) { - return symbolTable.addConstantMethodType(methodDescriptor).index; - } - - /** - * Adds a module reference to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param moduleName name of the module. - * @return the index of a new or already existing module reference item. - */ - public int newModule(final String moduleName) { - return symbolTable.addConstantModule(moduleName).index; - } - - /** - * Adds a package reference to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param packageName name of the package in its internal form. - * @return the index of a new or already existing module reference item. - */ - public int newPackage(final String packageName) { - return symbolTable.addConstantPackage(packageName).index; - } - - /** - * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool - * already contains a similar item. This method is intended for {@link Attribute} sub classes, - * and is normally not needed by class generators or adapters. - * - * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link - * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link - * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner the internal name of the field or method owner class. - * @param name the name of the field or method. - * @param descriptor the descriptor of the field or method. - * @return the index of a new or already existing method type reference item. - * @deprecated this method is superseded by {@link #newHandle(int, String, String, String, - * boolean)}. - */ - @Deprecated - public int newHandle( - final int tag, final String owner, final String name, final String descriptor) { - return newHandle(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE); - } - - /** - * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool - * already contains a similar item. This method is intended for {@link Attribute} sub classes, - * and is normally not needed by class generators or adapters. - * - * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link - * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link - * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner the internal name of the field or method owner class. - * @param name the name of the field or method. - * @param descriptor the descriptor of the field or method. - * @param isInterface true if the owner is an interface. - * @return the index of a new or already existing method type reference item. - */ - public int newHandle( - final int tag, - final String owner, - final String name, - final String descriptor, - final boolean isInterface) { - return symbolTable.addConstantMethodHandle(tag, owner, name, descriptor, isInterface).index; - } - - /** - * Adds an invokedynamic reference to the constant pool of the class being build. Does nothing if - * the constant pool already contains a similar item. This method is intended for {@link - * Attribute} sub classes, and is normally not needed by class generators or adapters. - * - * @param name name of the invoked method. - * @param descriptor descriptor of the invoke method. - * @param bootstrapMethodHandle the bootstrap method. - * @param bootstrapMethodArguments the bootstrap method constant arguments. - * @return the index of a new or already existing invokedynamic reference item. - */ - public int newInvokeDynamic( - final String name, - final String descriptor, - final Handle bootstrapMethodHandle, - final Object... bootstrapMethodArguments) { - return symbolTable.addConstantInvokeDynamic( - name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments) - .index; - } - - /** - * Adds a field reference to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param owner the internal name of the field's owner class. - * @param name the field's name. - * @param descriptor the field's descriptor. - * @return the index of a new or already existing field reference item. - */ - public int newField(final String owner, final String name, final String descriptor) { - return symbolTable.addConstantFieldref(owner, name, descriptor).index; - } - - /** - * Adds a method reference to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param owner the internal name of the method's owner class. - * @param name the method's name. - * @param descriptor the method's descriptor. - * @param isInterface true if owner is an interface. - * @return the index of a new or already existing method reference item. - */ - public int newMethod( - final String owner, final String name, final String descriptor, final boolean isInterface) { - return symbolTable.addConstantMethodref(owner, name, descriptor, isInterface).index; - } - - /** - * Adds a name and type to the constant pool of the class being build. Does nothing if the - * constant pool already contains a similar item. This method is intended for {@link Attribute} - * sub classes, and is normally not needed by class generators or adapters. - * - * @param name a name. - * @param descriptor a type descriptor. - * @return the index of a new or already existing name and type item. - */ - public int newNameType(final String name, final String descriptor) { - return symbolTable.addConstantNameAndType(name, descriptor); - } - - // ----------------------------------------------------------------------------------------------- - // Default method to compute common super classes when computing stack map frames - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the common super type of the two given types. The default implementation of this method - * loads the two given classes and uses the java.lang.Class methods to find the common - * super class. It can be overridden to compute this common super type in other ways, in - * particular without actually loading any class, or to take into account the class that is - * currently being generated by this ClassWriter, which can of course not be loaded since it is - * under construction. - * - * @param type1 the internal name of a class. - * @param type2 the internal name of another class. - * @return the internal name of the common super class of the two given classes. - */ - protected String getCommonSuperClass(final String type1, final String type2) { - ClassLoader classLoader = getClass().getClassLoader(); - Class class1; - try { - class1 = Class.forName(type1.replace('/', '.'), false, classLoader); - } catch (Exception e) { - throw new TypeNotPresentException(type1, e); - } - Class class2; - try { - class2 = Class.forName(type2.replace('/', '.'), false, classLoader); - } catch (Exception e) { - throw new TypeNotPresentException(type2, e); - } - if (class1.isAssignableFrom(class2)) { - return type1; - } - if (class2.isAssignableFrom(class1)) { - return type2; - } - if (class1.isInterface() || class2.isInterface()) { - return "java/lang/Object"; - } else { - do { - class1 = class1.getSuperclass(); - } while (!class1.isAssignableFrom(class2)); - return class1.getName().replace('.', '/'); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Constants.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Constants.java deleted file mode 100644 index 74630dbf18..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Constants.java +++ /dev/null @@ -1,175 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * Defines additional JVM opcodes, access flags and constants which are not part of the ASM public - * API. - * - * @see JVMS 6 - * @author Eric Bruneton - */ -final class Constants implements Opcodes { - - private Constants() {} - - // The ClassFile attribute names, in the order they are defined in - // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300. - - static final String CONSTANT_VALUE = "ConstantValue"; - static final String CODE = "Code"; - static final String STACK_MAP_TABLE = "StackMapTable"; - static final String EXCEPTIONS = "Exceptions"; - static final String INNER_CLASSES = "InnerClasses"; - static final String ENCLOSING_METHOD = "EnclosingMethod"; - static final String SYNTHETIC = "Synthetic"; - static final String SIGNATURE = "Signature"; - static final String SOURCE_FILE = "SourceFile"; - static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension"; - static final String LINE_NUMBER_TABLE = "LineNumberTable"; - static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable"; - static final String LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; - static final String DEPRECATED = "Deprecated"; - static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; - static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; - static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; - static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = - "RuntimeInvisibleParameterAnnotations"; - static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; - static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; - static final String ANNOTATION_DEFAULT = "AnnotationDefault"; - static final String BOOTSTRAP_METHODS = "BootstrapMethods"; - static final String METHOD_PARAMETERS = "MethodParameters"; - static final String MODULE = "Module"; - static final String MODULE_PACKAGES = "ModulePackages"; - static final String MODULE_MAIN_CLASS = "ModuleMainClass"; - - // ASM specific access flags. - // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard - // access flags, and also to make sure that these flags are automatically filtered out when - // written in class files (because access flags are stored using 16 bits only). - - static final int ACC_CONSTRUCTOR = 0x40000; // method access flag. - - // ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}. - - /** - * A frame inserted between already existing frames. This internal stack map frame type (in - * addition to the ones declared in {@link Opcodes}) can only be used if the frame content can be - * computed from the previous existing frame and from the instructions between this existing frame - * and the inserted one, without any knowledge of the type hierarchy. This kind of frame is only - * used when an unconditional jump is inserted in a method while expanding an ASM specific - * instruction. Keep in sync with Opcodes.java. - */ - static final int F_INSERT = 256; - - // The JVM opcode values which are not part of the ASM public API. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html. - - static final int LDC_W = 19; - static final int LDC2_W = 20; - static final int ILOAD_0 = 26; - static final int ILOAD_1 = 27; - static final int ILOAD_2 = 28; - static final int ILOAD_3 = 29; - static final int LLOAD_0 = 30; - static final int LLOAD_1 = 31; - static final int LLOAD_2 = 32; - static final int LLOAD_3 = 33; - static final int FLOAD_0 = 34; - static final int FLOAD_1 = 35; - static final int FLOAD_2 = 36; - static final int FLOAD_3 = 37; - static final int DLOAD_0 = 38; - static final int DLOAD_1 = 39; - static final int DLOAD_2 = 40; - static final int DLOAD_3 = 41; - static final int ALOAD_0 = 42; - static final int ALOAD_1 = 43; - static final int ALOAD_2 = 44; - static final int ALOAD_3 = 45; - static final int ISTORE_0 = 59; - static final int ISTORE_1 = 60; - static final int ISTORE_2 = 61; - static final int ISTORE_3 = 62; - static final int LSTORE_0 = 63; - static final int LSTORE_1 = 64; - static final int LSTORE_2 = 65; - static final int LSTORE_3 = 66; - static final int FSTORE_0 = 67; - static final int FSTORE_1 = 68; - static final int FSTORE_2 = 69; - static final int FSTORE_3 = 70; - static final int DSTORE_0 = 71; - static final int DSTORE_1 = 72; - static final int DSTORE_2 = 73; - static final int DSTORE_3 = 74; - static final int ASTORE_0 = 75; - static final int ASTORE_1 = 76; - static final int ASTORE_2 = 77; - static final int ASTORE_3 = 78; - static final int WIDE = 196; - static final int GOTO_W = 200; - static final int JSR_W = 201; - - // Constants to convert between normal and wide jump instructions. - - // The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP. - static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO; - - // Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa. - - // The delta between the ASM_IFEQ, ..., ASM_IF_ACMPNE, ASM_GOTO and ASM_JSR opcodes - // and IFEQ, ..., IF_ACMPNE, GOTO and JSR. - static final int ASM_OPCODE_DELTA = 49; - - // The delta between the ASM_IFNULL and ASM_IFNONNULL opcodes and IFNULL and IFNONNULL. - static final int ASM_IFNULL_OPCODE_DELTA = 20; - - // ASM specific opcodes, used for long forward jump instructions. - - static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA; - static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA; - static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA; - static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA; - static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA; - static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA; - static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA; - static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA; - static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA; - static final int ASM_JSR = JSR + ASM_OPCODE_DELTA; - static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA; - static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA; - static final int ASM_GOTO_W = 220; -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/CurrentFrame.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/CurrentFrame.java deleted file mode 100644 index b4fbf57e1a..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/CurrentFrame.java +++ /dev/null @@ -1,56 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. - -package bsh.org.objectweb.asm; - -/** - * Information about the input stack map frame at the "current" instruction of a method. This is - * implemented as a Frame subclass for a "basic block" containing only one instruction. - * - * @author Eric Bruneton - */ -final class CurrentFrame extends Frame { - - CurrentFrame(final Label owner) { - super(owner); - } - - /** - * Sets this CurrentFrame to the input stack map frame of the next "current" instruction, i.e. the - * instruction just after the given one. It is assumed that the value of this object when this - * method is called is the stack map frame status just before the given instruction is executed. - */ - @Override - void execute( - final int opcode, final int arg, final Symbol symbolArg, final SymbolTable symbolTable) { - super.execute(opcode, arg, symbolArg, symbolTable); - Frame successor = new Frame(null); - merge(symbolTable, successor, 0); - copyFrom(successor); - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Edge.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Edge.java deleted file mode 100644 index 87ccd7c7f6..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Edge.java +++ /dev/null @@ -1,91 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * An edge in the control flow graph of a method. Each node of this graph is a basic block, - * represented with the Label corresponding to its first instruction. Each edge goes from one node - * to another, i.e. from one basic block to another (called the predecessor and successor blocks, - * respectively). An edge corresponds either to a jump or ret instruction or to an exception - * handler. - * - * @see Label - * @author Eric Bruneton - */ -final class Edge { - - /** - * A control flow graph edge corresponding to a jump or ret instruction. Only used with {@link - * ClassWriter#COMPUTE_FRAMES}. - */ - static final int JUMP = 0; - - /** - * A control flow graph edge corresponding to an exception handler. Only used with {@link - * ClassWriter#COMPUTE_MAXS}. - */ - static final int EXCEPTION = 0x7FFFFFFF; - - /** - * Information about this control flow graph edge. - * - *
    - *
  • If {@link ClassWriter#COMPUTE_MAXS} is used, this field contains either a stack size - * delta (for an edge corresponding to a jump instruction), or the value EXCEPTION (for an - * edge corresponding to an exception handler). The stack size delta is the stack size just - * after the jump instruction, minus the stack size at the beginning of the predecessor - * basic block, i.e. the one containing the jump instruction. - *
  • If {@link ClassWriter#COMPUTE_FRAMES} is used, this field contains either the value JUMP - * (for an edge corresponding to a jump instruction), or the index, in the {@link - * ClassWriter} type table, of the exception type that is handled (for an edge corresponding - * to an exception handler). - *
- */ - final int info; - - /** The successor block of this control flow graph edge. */ - final Label successor; - - /** - * The next edge in the list of outgoing edges of a basic block. See {@link Label#outgoingEdges}. - */ - Edge nextEdge; - - /** - * Constructs a new Edge. - * - * @param info see {@link #info}. - * @param successor see {@link #successor}. - * @param nextEdge see {@link #nextEdge}. - */ - Edge(final int info, final Label successor, final Edge nextEdge) { - this.info = info; - this.successor = successor; - this.nextEdge = nextEdge; - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldVisitor.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldVisitor.java deleted file mode 100644 index 1b4b8632a2..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldVisitor.java +++ /dev/null @@ -1,98 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A visitor to visit a Java field. The methods of this class must be called in the following order: - * ( visitAnnotation | visitTypeAnnotation | visitAttribute )* - * visitEnd. - * - * @author Eric Bruneton - */ -public abstract class FieldVisitor { - - /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7_EXPERIMENTAL}. - */ - protected final int api; - - /** The field visitor to which this visitor must delegate method calls. May be null. */ - protected FieldVisitor fv; - - /** - * Constructs a new {@link FieldVisitor}. - * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link - * Opcodes#ASM7_EXPERIMENTAL}. - */ - public FieldVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link FieldVisitor}. - * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link - * Opcodes#ASM7_EXPERIMENTAL}. - * @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be - * null. - */ - public FieldVisitor(final int api, final FieldVisitor fieldVisitor) { - if (api != Opcodes.ASM6 - && api != Opcodes.ASM5 - && api != Opcodes.ASM4) { - throw new IllegalArgumentException(); - } - this.api = api; - this.fv = fieldVisitor; - } - - /** - * Visits a non standard attribute of the field. - * - * @param attribute an attribute. - */ - public void visitAttribute(final Attribute attribute) { - if (fv != null) { - fv.visitAttribute(attribute); - } - } - - /** - * Visits the end of the field. This method, which is the last one to be called, is used to inform - * the visitor that all the annotations and attributes of the field have been visited. - */ - public void visitEnd() { - if (fv != null) { - fv.visitEnd(); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldWriter.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldWriter.java deleted file mode 100644 index edaae01bf1..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/FieldWriter.java +++ /dev/null @@ -1,238 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A {@link FieldVisitor} that generates a corresponding 'field_info' structure, as defined in the - * Java Virtual Machine Specification (JVMS). - * - * @see JVMS - * 4.5 - * @author Eric Bruneton - */ -final class FieldWriter extends FieldVisitor { - - /** Where the constants used in this FieldWriter must be stored. */ - private final SymbolTable symbolTable; - - // Note: fields are ordered as in the field_info structure, and those related to attributes are - // ordered as in Section 4.7 of the JVMS. - - /** - * The access_flags field of the field_info JVMS structure. This field can contain ASM specific - * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the - * ClassFile structure. - */ - private final int accessFlags; - - /** The name_index field of the field_info JVMS structure. */ - private final int nameIndex; - - /** The descriptor_index field of the field_info JVMS structure. */ - private final int descriptorIndex; - - /** - * The signature_index field of the Signature attribute of this field_info, or 0 if there is no - * Signature attribute. - */ - private int signatureIndex; - - /** - * The constantvalue_index field of the ConstantValue attribute of this field_info, or 0 if there - * is no ConstantValue attribute. - */ - private int constantValueIndex; - - /** - * The first non standard attribute of this field. The next ones can be accessed with the {@link - * Attribute#nextAttribute} field. May be null. - * - *

WARNING: this list stores the attributes in the reverse order of their visit. - * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link - * #putFieldInfo} method writes the attributes in the order defined by this list, i.e. in the - * reverse order specified by the user. - */ - private Attribute firstAttribute; - - // ----------------------------------------------------------------------------------------------- - // Constructor - // ----------------------------------------------------------------------------------------------- - - /** - * Constructs a new {@link FieldWriter}. - * - * @param symbolTable where the constants used in this FieldWriter must be stored. - * @param access the field's access flags (see {@link Opcodes}). - * @param name the field's name. - * @param descriptor the field's descriptor (see {@link Type}). - * @param signature the field's signature. May be null. - * @param constantValue the field's constant value. May be null. - */ - FieldWriter( - final SymbolTable symbolTable, - final int access, - final String name, - final String descriptor, - final String signature, - final Object constantValue) { - super(Opcodes.ASM6); - this.symbolTable = symbolTable; - this.accessFlags = access; - this.nameIndex = symbolTable.addConstantUtf8(name); - this.descriptorIndex = symbolTable.addConstantUtf8(descriptor); - if (signature != null) { - this.signatureIndex = symbolTable.addConstantUtf8(signature); - } - if (constantValue != null) { - this.constantValueIndex = symbolTable.addConstant(constantValue).index; - } - } - - // ----------------------------------------------------------------------------------------------- - // Implementation of the FieldVisitor abstract class - // ----------------------------------------------------------------------------------------------- - - @Override - public void visitAttribute(final Attribute attribute) { - // Store the attributes in the reverse order of their visit by this method. - attribute.nextAttribute = firstAttribute; - firstAttribute = attribute; - } - - @Override - public void visitEnd() { - // Nothing to do. - } - - // ----------------------------------------------------------------------------------------------- - // Utility methods - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the size of the field_info JVMS structure generated by this FieldWriter. Also adds the - * names of the attributes of this field in the constant pool. - * - * @return the size in bytes of the field_info JVMS structure. - */ - int computeFieldInfoSize() { - // The access_flags, name_index, descriptor_index and attributes_count fields use 8 bytes. - int size = 8; - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - if (constantValueIndex != 0) { - // ConstantValue attributes always use 8 bytes. - symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE); - size += 8; - } - // Before Java 1.5, synthetic fields are represented with a Synthetic attribute. - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 - && symbolTable.getMajorVersion() < Opcodes.V1_5) { - // Synthetic attributes always use 6 bytes. - symbolTable.addConstantUtf8(Constants.SYNTHETIC); - size += 6; - } - if (signatureIndex != 0) { - // Signature attributes always use 8 bytes. - symbolTable.addConstantUtf8(Constants.SIGNATURE); - size += 8; - } - // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead. - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - // Deprecated attributes always use 6 bytes. - symbolTable.addConstantUtf8(Constants.DEPRECATED); - size += 6; - } - if (firstAttribute != null) { - size += firstAttribute.computeAttributesSize(symbolTable); - } - return size; - } - - /** - * Puts the content of the field_info JVMS structure generated by this FieldWriter into the given - * ByteVector. - * - * @param output where the field_info structure must be put. - */ - void putFieldInfo(final ByteVector output) { - boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; - // Put the access_flags, name_index and descriptor_index fields. - int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0; - output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex); - // Compute and put the attributes_count field. - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - int attributesCount = 0; - if (constantValueIndex != 0) { - ++attributesCount; - } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - ++attributesCount; - } - if (signatureIndex != 0) { - ++attributesCount; - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - ++attributesCount; - } - if (firstAttribute != null) { - attributesCount += firstAttribute.getAttributeCount(); - } - output.putShort(attributesCount); - // Put the field_info attributes. - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - if (constantValueIndex != 0) { - output - .putShort(symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE)) - .putInt(2) - .putShort(constantValueIndex); - } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); - } - if (signatureIndex != 0) { - output - .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) - .putInt(2) - .putShort(signatureIndex); - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); - } - if (firstAttribute != null) { - firstAttribute.putAttributes(symbolTable, output); - } - } - - /** - * Collects the attributes of this field into the given set of attribute prototypes. - * - * @param attributePrototypes a set of attribute prototypes. - */ - final void collectAttributePrototypes(final Attribute.Set attributePrototypes) { - attributePrototypes.addAttributes(firstAttribute); - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Frame.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Frame.java deleted file mode 100644 index 22d1a32a4c..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Frame.java +++ /dev/null @@ -1,1467 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * The input and output stack map frames of a basic block. - * - *

Stack map frames are computed in two steps: - * - *

    - *
  • During the visit of each instruction in MethodWriter, the state of the frame at the end of - * the current basic block is updated by simulating the action of the instruction on the - * previous state of this so called "output frame". - *
  • After all instructions have been visited, a fix point algorithm is used in MethodWriter to - * compute the "input frame" of each basic block (i.e. the stack map frame at the beginning of - * the basic block). See {@link MethodWriter#computeAllFrames}. - *
- * - *

Output stack map frames are computed relatively to the input frame of the basic block, which - * is not yet known when output frames are computed. It is therefore necessary to be able to - * represent abstract types such as "the type at position x in the input frame locals" or "the type - * at position x from the top of the input frame stack" or even "the type at position x in the input - * frame, with y more (or less) array dimensions". This explains the rather complicated type format - * used in this class, explained below. - * - *

The local variables and the operand stack of input and output frames contain values called - * "abstract types" hereafter. An abstract type is represented with 4 fields named DIM, KIND, FLAGS - * and VALUE, packed in a single int value for better performance and memory efficiency: - * - *

- *   =====================================
- *   |.DIM|KIND|FLAG|...............VALUE|
- *   =====================================
- * 
- * - *
    - *
  • the DIM field, stored in the 4 most significant bits, is a signed number of array - * dimensions (from -8 to 7, included). It can be retrieved with {@link #DIM_MASK} and a right - * shift of {@link #DIM_SHIFT}. - *
  • the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be - * retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link - * #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND} - * or {@link #STACK_KIND}. - *
  • the FLAGS field, stored in 4 bits, contains up to 4 boolean flags. Currently only one flag - * is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}. - *
  • the VALUE field, stored in the remaining 20 bits, contains either - *
      - *
    • one of the constants {@link #ITEM_TOP}, {@link #ITEM_ASM_BOOLEAN}, {@link - * #ITEM_ASM_BYTE}, {@link #ITEM_ASM_CHAR} or {@link #ITEM_ASM_SHORT}, {@link - * #ITEM_INTEGER}, {@link #ITEM_FLOAT}, {@link #ITEM_LONG}, {@link #ITEM_DOUBLE}, {@link - * #ITEM_NULL} or {@link #ITEM_UNINITIALIZED_THIS}, if KIND is equal to {@link - * #CONSTANT_KIND}. - *
    • the index of a {@link Symbol#TYPE_TAG} {@link Symbol} in the type table of a {@link - * SymbolTable}, if KIND is equal to {@link #REFERENCE_KIND}. - *
    • the index of an {@link Symbol#UNINITIALIZED_TYPE_TAG} {@link Symbol} in the type - * table of a SymbolTable, if KIND is equal to {@link #UNINITIALIZED_KIND}. - *
    • the index of a local variable in the input stack frame, if KIND is equal to {@link - * #LOCAL_KIND}. - *
    • a position relatively to the top of the stack of the input stack frame, if KIND is - * equal to {@link #STACK_KIND}, - *
    - *
- * - *

Output frames can contain abstract types of any kind and with a positive or negative array - * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid - * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or - * UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type - * table contains only internal type names (array type descriptors are forbidden - array dimensions - * must be represented through the DIM field). - * - *

The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE + - * TOP), for local variables as well as in the operand stack. This is necessary to be able to - * simulate DUPx_y instructions, whose effect would be dependent on the concrete types represented - * by the abstract types in the stack (which are not always known). - * - * @author Eric Bruneton - */ -class Frame { - - // Constants used in the StackMapTable attribute. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4. - - static final int SAME_FRAME = 0; - static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; - static final int RESERVED = 128; - static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; - static final int CHOP_FRAME = 248; - static final int SAME_FRAME_EXTENDED = 251; - static final int APPEND_FRAME = 252; - static final int FULL_FRAME = 255; - - static final int ITEM_TOP = 0; - static final int ITEM_INTEGER = 1; - static final int ITEM_FLOAT = 2; - static final int ITEM_DOUBLE = 3; - static final int ITEM_LONG = 4; - static final int ITEM_NULL = 5; - static final int ITEM_UNINITIALIZED_THIS = 6; - static final int ITEM_OBJECT = 7; - static final int ITEM_UNINITIALIZED = 8; - // Additional, ASM specific constants used in abstract types below. - private static final int ITEM_ASM_BOOLEAN = 9; - private static final int ITEM_ASM_BYTE = 10; - private static final int ITEM_ASM_CHAR = 11; - private static final int ITEM_ASM_SHORT = 12; - - // Bitmasks to get each field of an abstract type. - - private static final int DIM_MASK = 0xF0000000; - private static final int KIND_MASK = 0x0F000000; - private static final int FLAGS_MASK = 0x00F00000; - private static final int VALUE_MASK = 0x000FFFFF; - - // Constants to manipulate the DIM field of an abstract type. - - /** The number of right shift bits to use to get the array dimensions of an abstract type. */ - private static final int DIM_SHIFT = 28; - - /** The constant to be added to an abstract type to get one with one more array dimension. */ - private static final int ARRAY_OF = +1 << DIM_SHIFT; - - /** The constant to be added to an abstract type to get one with one less array dimension. */ - private static final int ELEMENT_OF = -1 << DIM_SHIFT; - - // Possible values for the KIND field of an abstract type. - - private static final int CONSTANT_KIND = 0x01000000; - private static final int REFERENCE_KIND = 0x02000000; - private static final int UNINITIALIZED_KIND = 0x03000000; - private static final int LOCAL_KIND = 0x04000000; - private static final int STACK_KIND = 0x05000000; - - // Possible flags for the FLAGS field of an abstract type. - - /** - * A flag used for LOCAL_KIND and STACK_KIND abstract types, indicating that if the resolved, - * concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been - * partially overridden with an xSTORE instruction). - */ - private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK; - - // Useful predefined abstract types (all the possible CONSTANT_KIND types). - - private static final int TOP = CONSTANT_KIND | ITEM_TOP; - private static final int BOOLEAN = CONSTANT_KIND | ITEM_ASM_BOOLEAN; - private static final int BYTE = CONSTANT_KIND | ITEM_ASM_BYTE; - private static final int CHAR = CONSTANT_KIND | ITEM_ASM_CHAR; - private static final int SHORT = CONSTANT_KIND | ITEM_ASM_SHORT; - private static final int INTEGER = CONSTANT_KIND | ITEM_INTEGER; - private static final int FLOAT = CONSTANT_KIND | ITEM_FLOAT; - private static final int LONG = CONSTANT_KIND | ITEM_LONG; - private static final int DOUBLE = CONSTANT_KIND | ITEM_DOUBLE; - private static final int NULL = CONSTANT_KIND | ITEM_NULL; - private static final int UNINITIALIZED_THIS = CONSTANT_KIND | ITEM_UNINITIALIZED_THIS; - - // ----------------------------------------------------------------------------------------------- - // Instance fields - // ----------------------------------------------------------------------------------------------- - - /** The basic block to which these input and output stack map frames correspond. */ - Label owner; - - /** The input stack map frame locals. This is an array of abstract types. */ - private int[] inputLocals; - - /** The input stack map frame stack. This is an array of abstract types. */ - private int[] inputStack; - - /** The output stack map frame locals. This is an array of abstract types. */ - private int[] outputLocals; - - /** The output stack map frame stack. This is an array of abstract types. */ - private int[] outputStack; - - /** - * The start of the output stack, relatively to the input stack. This offset is always negative or - * null. A null offset means that the output stack must be appended to the input stack. A -n - * offset means that the first n output stack elements must replace the top n input stack - * elements, and that the other elements must be appended to the input stack. - */ - private short outputStackStart; - - /** The index of the top stack element in {@link #outputStack}. */ - private short outputStackTop; - - /** The number of types that are initialized in the basic block. See {@link #initializations}. */ - private int initializationCount; - - /** - * The abstract types that are initialized in the basic block. A constructor invocation on an - * UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace every occurrence of this - * type in the local variables and in the operand stack. This cannot be done during the first step - * of the algorithm since, during this step, the local variables and the operand stack types are - * still abstract. It is therefore necessary to store the abstract types of the constructors which - * are invoked in the basic block, in order to do this replacement during the second step of the - * algorithm, where the frames are fully computed. Note that this array can contain abstract types - * that are relative to the input locals or to the input stack. - */ - private int[] initializations; - - // ----------------------------------------------------------------------------------------------- - // Static methods to get abstract types from other type formats - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the abstract type corresponding to the given public API frame element type. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param type a frame element type described using the same format as in {@link - * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link - * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or - * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating - * a NEW instruction (for uninitialized types). - * @return the abstract type corresponding to the given frame element type. - */ - static int getAbstractTypeFromApiFormat(final SymbolTable symbolTable, final Object type) { - if (type instanceof Integer) { - return CONSTANT_KIND | ((Integer) type).intValue(); - } else if (type instanceof String) { - String descriptor = Type.getObjectType((String) type).getDescriptor(); - return getAbstractTypeFromDescriptor(symbolTable, descriptor, 0); - } else { - return UNINITIALIZED_KIND - | symbolTable.addUninitializedType("", ((Label) type).bytecodeOffset); - } - } - - /** - * Returns the abstract type corresponding to the internal name of a class. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param internalName the internal name of a class. This must not be an array type - * descriptor. - * @return the abstract type value corresponding to the given internal name. - */ - static int getAbstractTypeFromInternalName( - final SymbolTable symbolTable, final String internalName) { - return REFERENCE_KIND | symbolTable.addType(internalName); - } - - /** - * Returns the abstract type corresponding to the given type descriptor. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param buffer a string ending with a type descriptor. - * @param offset the start offset of the type descriptor in buffer. - * @return the abstract type corresponding to the given type descriptor. - */ - private static int getAbstractTypeFromDescriptor( - final SymbolTable symbolTable, final String buffer, final int offset) { - String internalName; - switch (buffer.charAt(offset)) { - case 'V': - return 0; - case 'Z': - case 'C': - case 'B': - case 'S': - case 'I': - return INTEGER; - case 'F': - return FLOAT; - case 'J': - return LONG; - case 'D': - return DOUBLE; - case 'L': - internalName = buffer.substring(offset + 1, buffer.length() - 1); - return REFERENCE_KIND | symbolTable.addType(internalName); - case '[': - int elementDescriptorOffset = offset + 1; - while (buffer.charAt(elementDescriptorOffset) == '[') { - ++elementDescriptorOffset; - } - int typeValue; - switch (buffer.charAt(elementDescriptorOffset)) { - case 'Z': - typeValue = BOOLEAN; - break; - case 'C': - typeValue = CHAR; - break; - case 'B': - typeValue = BYTE; - break; - case 'S': - typeValue = SHORT; - break; - case 'I': - typeValue = INTEGER; - break; - case 'F': - typeValue = FLOAT; - break; - case 'J': - typeValue = LONG; - break; - case 'D': - typeValue = DOUBLE; - break; - case 'L': - internalName = buffer.substring(elementDescriptorOffset + 1, buffer.length() - 1); - typeValue = REFERENCE_KIND | symbolTable.addType(internalName); - break; - default: - throw new IllegalArgumentException(); - } - return ((elementDescriptorOffset - offset) << DIM_SHIFT) | typeValue; - default: - throw new IllegalArgumentException(); - } - } - - // ----------------------------------------------------------------------------------------------- - // Constructor - // ----------------------------------------------------------------------------------------------- - - /** - * Constructs a new Frame. - * - * @param owner the basic block to which these input and output stack map frames correspond. - */ - Frame(final Label owner) { - this.owner = owner; - } - - /** - * Sets this frame to the value of the given frame. - * - *

WARNING: after this method is called the two frames share the same data structures. It is - * recommended to discard the given frame to avoid unexpected side effects. - * - * @param frame The new frame value. - */ - final void copyFrom(final Frame frame) { - inputLocals = frame.inputLocals; - inputStack = frame.inputStack; - outputStackStart = 0; - outputLocals = frame.outputLocals; - outputStack = frame.outputStack; - outputStackTop = frame.outputStackTop; - initializationCount = frame.initializationCount; - initializations = frame.initializations; - } - - // ----------------------------------------------------------------------------------------------- - // Methods related to the input frame - // ----------------------------------------------------------------------------------------------- - - /** - * Sets the input frame from the given method description. This method is used to initialize the - * first frame of a method, which is implicit (i.e. not stored explicitly in the StackMapTable - * attribute). - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param access the method's access flags. - * @param descriptor the method descriptor. - * @param maxLocals the maximum number of local variables of the method. - */ - final void setInputFrameFromDescriptor( - final SymbolTable symbolTable, - final int access, - final String descriptor, - final int maxLocals) { - inputLocals = new int[maxLocals]; - inputStack = new int[0]; - int inputLocalIndex = 0; - if ((access & Opcodes.ACC_STATIC) == 0) { - if ((access & Constants.ACC_CONSTRUCTOR) == 0) { - inputLocals[inputLocalIndex++] = - REFERENCE_KIND | symbolTable.addType(symbolTable.getClassName()); - } else { - inputLocals[inputLocalIndex++] = UNINITIALIZED_THIS; - } - } - for (Type argumentType : Type.getArgumentTypes(descriptor)) { - int abstractType = - getAbstractTypeFromDescriptor(symbolTable, argumentType.getDescriptor(), 0); - inputLocals[inputLocalIndex++] = abstractType; - if (abstractType == LONG || abstractType == DOUBLE) { - inputLocals[inputLocalIndex++] = TOP; - } - } - while (inputLocalIndex < maxLocals) { - inputLocals[inputLocalIndex++] = TOP; - } - } - - /** - * Sets the input frame from the given public API frame description. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param nLocal the number of local variables. - * @param local the local variable types, described using the same format as in {@link - * MethodVisitor#visitFrame}. - * @param nStack the number of operand stack elements. - * @param stack the operand stack types, described using the same format as in {@link - * MethodVisitor#visitFrame}. - */ - final void setInputFrameFromApiFormat( - final SymbolTable symbolTable, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack) { - int inputLocalIndex = 0; - for (int i = 0; i < nLocal; ++i) { - inputLocals[inputLocalIndex++] = getAbstractTypeFromApiFormat(symbolTable, local[i]); - if (local[i] == Opcodes.LONG || local[i] == Opcodes.DOUBLE) { - inputLocals[inputLocalIndex++] = TOP; - } - } - while (inputLocalIndex < inputLocals.length) { - inputLocals[inputLocalIndex++] = TOP; - } - int nStackTop = 0; - for (int i = 0; i < nStack; ++i) { - if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) { - ++nStackTop; - } - } - inputStack = new int[nStack + nStackTop]; - int inputStackIndex = 0; - for (int i = 0; i < nStack; ++i) { - inputStack[inputStackIndex++] = getAbstractTypeFromApiFormat(symbolTable, stack[i]); - if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) { - inputStack[inputStackIndex++] = TOP; - } - } - outputStackTop = 0; - initializationCount = 0; - } - - final int getInputStackSize() { - return inputStack.length; - } - - // ----------------------------------------------------------------------------------------------- - // Methods related to the output frame - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the abstract type stored at the given local variable index in the output frame. - * - * @param localIndex the index of the local variable whose value must be returned. - * @return the abstract type stored at the given local variable index in the output frame. - */ - private int getLocal(final int localIndex) { - if (outputLocals == null || localIndex >= outputLocals.length) { - // If this local has never been assigned in this basic block, it is still equal to its value - // in the input frame. - return LOCAL_KIND | localIndex; - } else { - int abstractType = outputLocals[localIndex]; - if (abstractType == 0) { - // If this local has never been assigned in this basic block, so it is still equal to its - // value in the input frame. - abstractType = outputLocals[localIndex] = LOCAL_KIND | localIndex; - } - return abstractType; - } - } - - /** - * Replaces the abstract type stored at the given local variable index in the output frame. - * - * @param localIndex the index of the output frame local variable that must be set. - * @param abstractType the value that must be set. - */ - private void setLocal(final int localIndex, final int abstractType) { - // Create and/or resize the output local variables array if necessary. - if (outputLocals == null) { - outputLocals = new int[10]; - } - int outputLocalsLength = outputLocals.length; - if (localIndex >= outputLocalsLength) { - int[] newOutputLocals = new int[Math.max(localIndex + 1, 2 * outputLocalsLength)]; - System.arraycopy(outputLocals, 0, newOutputLocals, 0, outputLocalsLength); - outputLocals = newOutputLocals; - } - // Set the local variable. - outputLocals[localIndex] = abstractType; - } - - /** - * Pushes the given abstract type on the output frame stack. - * - * @param abstractType an abstract type. - */ - private void push(final int abstractType) { - // Create and/or resize the output stack array if necessary. - if (outputStack == null) { - outputStack = new int[10]; - } - int outputStackLength = outputStack.length; - if (outputStackTop >= outputStackLength) { - int[] newOutputStack = new int[Math.max(outputStackTop + 1, 2 * outputStackLength)]; - System.arraycopy(outputStack, 0, newOutputStack, 0, outputStackLength); - outputStack = newOutputStack; - } - // Pushes the abstract type on the output stack. - outputStack[outputStackTop++] = abstractType; - // Updates the maximum size reached by the output stack, if needed (note that this size is - // relative to the input stack size, which is not known yet). - short outputStackSize = (short) (outputStackStart + outputStackTop); - if (outputStackSize > owner.outputStackMax) { - owner.outputStackMax = outputStackSize; - } - } - - /** - * Pushes the abstract type corresponding to the given descriptor on the output frame stack. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param descriptor a type or method descriptor (in which case its return type is pushed). - */ - private void push(final SymbolTable symbolTable, final String descriptor) { - int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0; - int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset); - if (abstractType != 0) { - push(abstractType); - if (abstractType == LONG || abstractType == DOUBLE) { - push(TOP); - } - } - } - - /** - * Pops an abstract type from the output frame stack and returns its value. - * - * @return the abstract type that has been popped from the output frame stack. - */ - private int pop() { - if (outputStackTop > 0) { - return outputStack[--outputStackTop]; - } else { - // If the output frame stack is empty, pop from the input stack. - return STACK_KIND | -(--outputStackStart); - } - } - - /** - * Pops the given number of abstract types from the output frame stack. - * - * @param elements the number of abstract types that must be popped. - */ - private void pop(final int elements) { - if (outputStackTop >= elements) { - outputStackTop -= elements; - } else { - // If the number of elements to be popped is greater than the number of elements in the output - // stack, clear it, and pop the remaining elements from the input stack. - outputStackStart -= elements - outputStackTop; - outputStackTop = 0; - } - } - - /** - * Pops as many abstract types from the output frame stack as described by the given descriptor. - * - * @param descriptor a type or method descriptor (in which case its argument types are popped). - */ - private void pop(final String descriptor) { - char firstDescriptorChar = descriptor.charAt(0); - if (firstDescriptorChar == '(') { - pop((Type.getArgumentsAndReturnSizes(descriptor) >> 2) - 1); - } else if (firstDescriptorChar == 'J' || firstDescriptorChar == 'D') { - pop(2); - } else { - pop(1); - } - } - - // ----------------------------------------------------------------------------------------------- - // Methods to handle uninitialized types - // ----------------------------------------------------------------------------------------------- - - /** - * Adds an abstract type to the list of types on which a constructor is invoked in the basic - * block. - * - * @param abstractType an abstract type on a which a constructor is invoked. - */ - private void addInitializedType(final int abstractType) { - // Create and/or resize the initializations array if necessary. - if (initializations == null) { - initializations = new int[2]; - } - int initializationsLength = initializations.length; - if (initializationCount >= initializationsLength) { - int[] newInitializations = - new int[Math.max(initializationCount + 1, 2 * initializationsLength)]; - System.arraycopy(initializations, 0, newInitializations, 0, initializationsLength); - initializations = newInitializations; - } - // Store the abstract type. - initializations[initializationCount++] = abstractType; - } - - /** - * Returns the "initialized" abstract type corresponding to the given abstract type. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param abstractType an abstract type. - * @return the REFERENCE_KIND abstract type corresponding to abstractType if it is - * UNINITIALIZED_THIS or an UNINITIALIZED_KIND abstract type for one of the types on which a - * constructor is invoked in the basic block. Otherwise returns abstractType. - */ - private int getInitializedType(final SymbolTable symbolTable, final int abstractType) { - if (abstractType == UNINITIALIZED_THIS - || (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND) { - for (int i = 0; i < initializationCount; ++i) { - int initializedType = initializations[i]; - int dim = initializedType & DIM_MASK; - int kind = initializedType & KIND_MASK; - int value = initializedType & VALUE_MASK; - if (kind == LOCAL_KIND) { - initializedType = dim + inputLocals[value]; - } else if (kind == STACK_KIND) { - initializedType = dim + inputStack[inputStack.length - value]; - } - if (abstractType == initializedType) { - if (abstractType == UNINITIALIZED_THIS) { - return REFERENCE_KIND | symbolTable.addType(symbolTable.getClassName()); - } else { - return REFERENCE_KIND - | symbolTable.addType(symbolTable.getType(abstractType & VALUE_MASK).value); - } - } - } - } - return abstractType; - } - - // ----------------------------------------------------------------------------------------------- - // Main method, to simulate the execution of each instruction on the output frame - // ----------------------------------------------------------------------------------------------- - - /** - * Simulates the action of the given instruction on the output stack frame. - * - * @param opcode the opcode of the instruction. - * @param arg the numeric operand of the instruction, if any. - * @param argSymbol the Symbol operand of the instruction, if any. - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - */ - void execute( - final int opcode, final int arg, final Symbol argSymbol, final SymbolTable symbolTable) { - // Abstract types popped from the stack or read from local variables. - int abstractType1; - int abstractType2; - int abstractType3; - int abstractType4; - switch (opcode) { - case Opcodes.NOP: - case Opcodes.INEG: - case Opcodes.LNEG: - case Opcodes.FNEG: - case Opcodes.DNEG: - case Opcodes.I2B: - case Opcodes.I2C: - case Opcodes.I2S: - case Opcodes.GOTO: - case Opcodes.RETURN: - break; - case Opcodes.ACONST_NULL: - push(NULL); - break; - case Opcodes.ICONST_M1: - case Opcodes.ICONST_0: - case Opcodes.ICONST_1: - case Opcodes.ICONST_2: - case Opcodes.ICONST_3: - case Opcodes.ICONST_4: - case Opcodes.ICONST_5: - case Opcodes.BIPUSH: - case Opcodes.SIPUSH: - case Opcodes.ILOAD: - push(INTEGER); - break; - case Opcodes.LCONST_0: - case Opcodes.LCONST_1: - case Opcodes.LLOAD: - push(LONG); - push(TOP); - break; - case Opcodes.FCONST_0: - case Opcodes.FCONST_1: - case Opcodes.FCONST_2: - case Opcodes.FLOAD: - push(FLOAT); - break; - case Opcodes.DCONST_0: - case Opcodes.DCONST_1: - case Opcodes.DLOAD: - push(DOUBLE); - push(TOP); - break; - case Opcodes.LDC: - switch (argSymbol.tag) { - case Symbol.CONSTANT_INTEGER_TAG: - push(INTEGER); - break; - case Symbol.CONSTANT_LONG_TAG: - push(LONG); - push(TOP); - break; - case Symbol.CONSTANT_FLOAT_TAG: - push(FLOAT); - break; - case Symbol.CONSTANT_DOUBLE_TAG: - push(DOUBLE); - push(TOP); - break; - case Symbol.CONSTANT_CLASS_TAG: - push(REFERENCE_KIND | symbolTable.addType("java/lang/Class")); - break; - case Symbol.CONSTANT_STRING_TAG: - push(REFERENCE_KIND | symbolTable.addType("java/lang/String")); - break; - case Symbol.CONSTANT_METHOD_TYPE_TAG: - push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodType")); - break; - case Symbol.CONSTANT_METHOD_HANDLE_TAG: - push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodHandle")); - break; - case Symbol.CONSTANT_DYNAMIC_TAG: - push(symbolTable, argSymbol.value); - break; - default: - throw new AssertionError(); - } - break; - case Opcodes.ALOAD: - push(getLocal(arg)); - break; - case Opcodes.LALOAD: - case Opcodes.D2L: - pop(2); - push(LONG); - push(TOP); - break; - case Opcodes.DALOAD: - case Opcodes.L2D: - pop(2); - push(DOUBLE); - push(TOP); - break; - case Opcodes.AALOAD: - pop(1); - abstractType1 = pop(); - push(abstractType1 == NULL ? abstractType1 : ELEMENT_OF + abstractType1); - break; - case Opcodes.ISTORE: - case Opcodes.FSTORE: - case Opcodes.ASTORE: - abstractType1 = pop(); - setLocal(arg, abstractType1); - if (arg > 0) { - int previousLocalType = getLocal(arg - 1); - if (previousLocalType == LONG || previousLocalType == DOUBLE) { - setLocal(arg - 1, TOP); - } else if ((previousLocalType & KIND_MASK) == LOCAL_KIND - || (previousLocalType & KIND_MASK) == STACK_KIND) { - // The type of the previous local variable is not known yet, but if it later appears - // to be LONG or DOUBLE, we should then use TOP instead. - setLocal(arg - 1, previousLocalType | TOP_IF_LONG_OR_DOUBLE_FLAG); - } - } - break; - case Opcodes.LSTORE: - case Opcodes.DSTORE: - pop(1); - abstractType1 = pop(); - setLocal(arg, abstractType1); - setLocal(arg + 1, TOP); - if (arg > 0) { - int previousLocalType = getLocal(arg - 1); - if (previousLocalType == LONG || previousLocalType == DOUBLE) { - setLocal(arg - 1, TOP); - } else if ((previousLocalType & KIND_MASK) == LOCAL_KIND - || (previousLocalType & KIND_MASK) == STACK_KIND) { - // The type of the previous local variable is not known yet, but if it later appears - // to be LONG or DOUBLE, we should then use TOP instead. - setLocal(arg - 1, previousLocalType | TOP_IF_LONG_OR_DOUBLE_FLAG); - } - } - break; - case Opcodes.IASTORE: - case Opcodes.BASTORE: - case Opcodes.CASTORE: - case Opcodes.SASTORE: - case Opcodes.FASTORE: - case Opcodes.AASTORE: - pop(3); - break; - case Opcodes.LASTORE: - case Opcodes.DASTORE: - pop(4); - break; - case Opcodes.POP: - case Opcodes.IFEQ: - case Opcodes.IFNE: - case Opcodes.IFLT: - case Opcodes.IFGE: - case Opcodes.IFGT: - case Opcodes.IFLE: - case Opcodes.IRETURN: - case Opcodes.FRETURN: - case Opcodes.ARETURN: - case Opcodes.TABLESWITCH: - case Opcodes.LOOKUPSWITCH: - case Opcodes.ATHROW: - case Opcodes.MONITORENTER: - case Opcodes.MONITOREXIT: - case Opcodes.IFNULL: - case Opcodes.IFNONNULL: - pop(1); - break; - case Opcodes.POP2: - case Opcodes.IF_ICMPEQ: - case Opcodes.IF_ICMPNE: - case Opcodes.IF_ICMPLT: - case Opcodes.IF_ICMPGE: - case Opcodes.IF_ICMPGT: - case Opcodes.IF_ICMPLE: - case Opcodes.IF_ACMPEQ: - case Opcodes.IF_ACMPNE: - case Opcodes.LRETURN: - case Opcodes.DRETURN: - pop(2); - break; - case Opcodes.DUP: - abstractType1 = pop(); - push(abstractType1); - push(abstractType1); - break; - case Opcodes.DUP_X1: - abstractType1 = pop(); - abstractType2 = pop(); - push(abstractType1); - push(abstractType2); - push(abstractType1); - break; - case Opcodes.DUP_X2: - abstractType1 = pop(); - abstractType2 = pop(); - abstractType3 = pop(); - push(abstractType1); - push(abstractType3); - push(abstractType2); - push(abstractType1); - break; - case Opcodes.DUP2: - abstractType1 = pop(); - abstractType2 = pop(); - push(abstractType2); - push(abstractType1); - push(abstractType2); - push(abstractType1); - break; - case Opcodes.DUP2_X1: - abstractType1 = pop(); - abstractType2 = pop(); - abstractType3 = pop(); - push(abstractType2); - push(abstractType1); - push(abstractType3); - push(abstractType2); - push(abstractType1); - break; - case Opcodes.DUP2_X2: - abstractType1 = pop(); - abstractType2 = pop(); - abstractType3 = pop(); - abstractType4 = pop(); - push(abstractType2); - push(abstractType1); - push(abstractType4); - push(abstractType3); - push(abstractType2); - push(abstractType1); - break; - case Opcodes.SWAP: - abstractType1 = pop(); - abstractType2 = pop(); - push(abstractType1); - push(abstractType2); - break; - case Opcodes.IALOAD: - case Opcodes.BALOAD: - case Opcodes.CALOAD: - case Opcodes.SALOAD: - case Opcodes.IADD: - case Opcodes.ISUB: - case Opcodes.IMUL: - case Opcodes.IDIV: - case Opcodes.IREM: - case Opcodes.IAND: - case Opcodes.IOR: - case Opcodes.IXOR: - case Opcodes.ISHL: - case Opcodes.ISHR: - case Opcodes.IUSHR: - case Opcodes.L2I: - case Opcodes.D2I: - case Opcodes.FCMPL: - case Opcodes.FCMPG: - pop(2); - push(INTEGER); - break; - case Opcodes.LADD: - case Opcodes.LSUB: - case Opcodes.LMUL: - case Opcodes.LDIV: - case Opcodes.LREM: - case Opcodes.LAND: - case Opcodes.LOR: - case Opcodes.LXOR: - pop(4); - push(LONG); - push(TOP); - break; - case Opcodes.FALOAD: - case Opcodes.FADD: - case Opcodes.FSUB: - case Opcodes.FMUL: - case Opcodes.FDIV: - case Opcodes.FREM: - case Opcodes.L2F: - case Opcodes.D2F: - pop(2); - push(FLOAT); - break; - case Opcodes.DADD: - case Opcodes.DSUB: - case Opcodes.DMUL: - case Opcodes.DDIV: - case Opcodes.DREM: - pop(4); - push(DOUBLE); - push(TOP); - break; - case Opcodes.LSHL: - case Opcodes.LSHR: - case Opcodes.LUSHR: - pop(3); - push(LONG); - push(TOP); - break; - case Opcodes.IINC: - setLocal(arg, INTEGER); - break; - case Opcodes.I2L: - case Opcodes.F2L: - pop(1); - push(LONG); - push(TOP); - break; - case Opcodes.I2F: - pop(1); - push(FLOAT); - break; - case Opcodes.I2D: - case Opcodes.F2D: - pop(1); - push(DOUBLE); - push(TOP); - break; - case Opcodes.F2I: - case Opcodes.ARRAYLENGTH: - case Opcodes.INSTANCEOF: - pop(1); - push(INTEGER); - break; - case Opcodes.LCMP: - case Opcodes.DCMPL: - case Opcodes.DCMPG: - pop(4); - push(INTEGER); - break; - case Opcodes.JSR: - case Opcodes.RET: - throw new IllegalArgumentException("JSR/RET are not supported with computeFrames option"); - case Opcodes.GETSTATIC: - push(symbolTable, argSymbol.value); - break; - case Opcodes.PUTSTATIC: - pop(argSymbol.value); - break; - case Opcodes.GETFIELD: - pop(1); - push(symbolTable, argSymbol.value); - break; - case Opcodes.PUTFIELD: - pop(argSymbol.value); - pop(); - break; - case Opcodes.INVOKEVIRTUAL: - case Opcodes.INVOKESPECIAL: - case Opcodes.INVOKESTATIC: - case Opcodes.INVOKEINTERFACE: - pop(argSymbol.value); - if (opcode != Opcodes.INVOKESTATIC) { - abstractType1 = pop(); - if (opcode == Opcodes.INVOKESPECIAL && argSymbol.name.charAt(0) == '<') { - addInitializedType(abstractType1); - } - } - push(symbolTable, argSymbol.value); - break; - case Opcodes.INVOKEDYNAMIC: - pop(argSymbol.value); - push(symbolTable, argSymbol.value); - break; - case Opcodes.NEW: - push(UNINITIALIZED_KIND | symbolTable.addUninitializedType(argSymbol.value, arg)); - break; - case Opcodes.NEWARRAY: - pop(); - switch (arg) { - case Opcodes.T_BOOLEAN: - push(ARRAY_OF | BOOLEAN); - break; - case Opcodes.T_CHAR: - push(ARRAY_OF | CHAR); - break; - case Opcodes.T_BYTE: - push(ARRAY_OF | BYTE); - break; - case Opcodes.T_SHORT: - push(ARRAY_OF | SHORT); - break; - case Opcodes.T_INT: - push(ARRAY_OF | INTEGER); - break; - case Opcodes.T_FLOAT: - push(ARRAY_OF | FLOAT); - break; - case Opcodes.T_DOUBLE: - push(ARRAY_OF | DOUBLE); - break; - case Opcodes.T_LONG: - push(ARRAY_OF | LONG); - break; - default: - throw new IllegalArgumentException(); - } - break; - case Opcodes.ANEWARRAY: - String arrayElementType = argSymbol.value; - pop(); - if (arrayElementType.charAt(0) == '[') { - push(symbolTable, '[' + arrayElementType); - } else { - push(ARRAY_OF | REFERENCE_KIND | symbolTable.addType(arrayElementType)); - } - break; - case Opcodes.CHECKCAST: - String castType = argSymbol.value; - pop(); - if (castType.charAt(0) == '[') { - push(symbolTable, castType); - } else { - push(REFERENCE_KIND | symbolTable.addType(castType)); - } - break; - case Opcodes.MULTIANEWARRAY: - pop(arg); - push(symbolTable, argSymbol.value); - break; - default: - throw new IllegalArgumentException(); - } - } - - // ----------------------------------------------------------------------------------------------- - // Frame merging methods, used in the second step of the stack map frame computation algorithm - // ----------------------------------------------------------------------------------------------- - - /** - * Merges the input frame of the given {@link Frame} with the input and output frames of this - * {@link Frame}. Returns true if the given frame has been changed by this operation (the - * input and output frames of this {@link Frame} are never changed). - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param dstFrame the {@link Frame} whose input frame must be updated. This should be the frame - * of a successor, in the control flow graph, of the basic block corresponding to this frame. - * @param catchTypeIndex if 'frame' corresponds to an exception handler basic block, the type - * table index of the caught exception type, otherwise 0. - * @return true if the input frame of 'frame' has been changed by this operation. - */ - final boolean merge( - final SymbolTable symbolTable, final Frame dstFrame, final int catchTypeIndex) { - boolean frameChanged = false; - - // Compute the concrete types of the local variables at the end of the basic block corresponding - // to this frame, by resolving its abstract output types, and merge these concrete types with - // those of the local variables in the input frame of dstFrame. - int nLocal = inputLocals.length; - int nStack = inputStack.length; - if (dstFrame.inputLocals == null) { - dstFrame.inputLocals = new int[nLocal]; - frameChanged = true; - } - for (int i = 0; i < nLocal; ++i) { - int concreteOutputType; - if (outputLocals != null && i < outputLocals.length) { - int abstractOutputType = outputLocals[i]; - if (abstractOutputType == 0) { - // If the local variable has never been assigned in this basic block, it is equal to its - // value at the beginning of the block. - concreteOutputType = inputLocals[i]; - } else { - int dim = abstractOutputType & DIM_MASK; - int kind = abstractOutputType & KIND_MASK; - if (kind == LOCAL_KIND) { - // By definition, a LOCAL_KIND type designates the concrete type of a local variable at - // the beginning of the basic block corresponding to this frame (which is known when - // this method is called, but was not when the abstract type was computed). - concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else if (kind == STACK_KIND) { - // By definition, a STACK_KIND type designates the concrete type of a local variable at - // the beginning of the basic block corresponding to this frame (which is known when - // this method is called, but was not when the abstract type was computed). - concreteOutputType = dim + inputStack[nStack - (abstractOutputType & VALUE_MASK)]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else { - concreteOutputType = abstractOutputType; - } - } - } else { - // If the local variable has never been assigned in this basic block, it is equal to its - // value at the beginning of the block. - concreteOutputType = inputLocals[i]; - } - // concreteOutputType might be an uninitialized type from the input locals or from the input - // stack. However, if a constructor has been called for this class type in the basic block, - // then this type is no longer uninitialized at the end of basic block. - if (initializations != null) { - concreteOutputType = getInitializedType(symbolTable, concreteOutputType); - } - frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputLocals, i); - } - - // If dstFrame is an exception handler block, it can be reached from any instruction of the - // basic block corresponding to this frame, in particular from the first one. Therefore, the - // input locals of dstFrame should be compatible (i.e. merged) with the input locals of this - // frame (and the input stack of dstFrame should be compatible, i.e. merged, with a one - // element stack containing the caught exception type). - if (catchTypeIndex > 0) { - for (int i = 0; i < nLocal; ++i) { - frameChanged |= merge(symbolTable, inputLocals[i], dstFrame.inputLocals, i); - } - if (dstFrame.inputStack == null) { - dstFrame.inputStack = new int[1]; - frameChanged = true; - } - frameChanged |= merge(symbolTable, catchTypeIndex, dstFrame.inputStack, 0); - return frameChanged; - } - - // Compute the concrete types of the stack operands at the end of the basic block corresponding - // to this frame, by resolving its abstract output types, and merge these concrete types with - // those of the stack operands in the input frame of dstFrame. - int nInputStack = inputStack.length + outputStackStart; - if (dstFrame.inputStack == null) { - dstFrame.inputStack = new int[nInputStack + outputStackTop]; - frameChanged = true; - } - // First, do this for the stack operands that have not been popped in the basic block - // corresponding to this frame, and which are therefore equal to their value in the input - // frame (except for uninitialized types, which may have been initialized). - for (int i = 0; i < nInputStack; ++i) { - int concreteOutputType = inputStack[i]; - if (initializations != null) { - concreteOutputType = getInitializedType(symbolTable, concreteOutputType); - } - frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputStack, i); - } - // Then, do this for the stack operands that have pushed in the basic block (this code is the - // same as the one above for local variables). - for (int i = 0; i < outputStackTop; ++i) { - int concreteOutputType; - int abstractOutputType = outputStack[i]; - int dim = abstractOutputType & DIM_MASK; - int kind = abstractOutputType & KIND_MASK; - if (kind == LOCAL_KIND) { - concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else if (kind == STACK_KIND) { - concreteOutputType = dim + inputStack[nStack - (abstractOutputType & VALUE_MASK)]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else { - concreteOutputType = abstractOutputType; - } - if (initializations != null) { - concreteOutputType = getInitializedType(symbolTable, concreteOutputType); - } - frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputStack, nInputStack + i); - } - return frameChanged; - } - - /** - * Merges the type at the given index in the given abstract type array with the given type. - * Returns true if the type array has been modified by this operation. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param sourceType the abstract type with which the abstract type array element must be merged. - * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link - * #UNINITIALIZED_KIND} kind, with positive or null array dimensions. - * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND}, - * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array - * dimensions. - * @param dstIndex the index of the type that must be merged in dstTypes. - * @return true if the type array has been modified by this operation. - */ - private static boolean merge( - final SymbolTable symbolTable, - final int sourceType, - final int[] dstTypes, - final int dstIndex) { - int dstType = dstTypes[dstIndex]; - if (dstType == sourceType) { - // If the types are equal, merge(sourceType, dstType) = dstType, so there is no change. - return false; - } - int srcType = sourceType; - if ((sourceType & ~DIM_MASK) == NULL) { - if (dstType == NULL) { - return false; - } - srcType = NULL; - } - if (dstType == 0) { - // If dstTypes[dstIndex] has never been assigned, merge(srcType, dstType) = srcType. - dstTypes[dstIndex] = srcType; - return true; - } - int mergedType; - if ((dstType & DIM_MASK) != 0 || (dstType & KIND_MASK) == REFERENCE_KIND) { - // If dstType is a reference type of any array dimension. - if (srcType == NULL) { - // If srcType is the NULL type, merge(srcType, dstType) = dstType, so there is no change. - return false; - } else if ((srcType & (DIM_MASK | KIND_MASK)) == (dstType & (DIM_MASK | KIND_MASK))) { - // If srcType has the same array dimension and the same kind as dstType. - if ((dstType & KIND_MASK) == REFERENCE_KIND) { - // If srcType and dstType are reference types with the same array dimension, - // merge(srcType, dstType) = dim(srcType) | common super class of srcType and dstType. - mergedType = - (srcType & DIM_MASK) - | REFERENCE_KIND - | symbolTable.addMergedType(srcType & VALUE_MASK, dstType & VALUE_MASK); - } else { - // If srcType and dstType are array types of equal dimension but different element types, - // merge(srcType, dstType) = dim(srcType) - 1 | java/lang/Object. - int mergedDim = ELEMENT_OF + (srcType & DIM_MASK); - mergedType = mergedDim | REFERENCE_KIND | symbolTable.addType("java/lang/Object"); - } - } else if ((srcType & DIM_MASK) != 0 || (srcType & KIND_MASK) == REFERENCE_KIND) { - // If srcType is any other reference or array type, - // merge(srcType, dstType) = min(srcDdim, dstDim) | java/lang/Object - // where srcDim is the array dimension of srcType, minus 1 if srcType is an array type - // with a non reference element type (and similarly for dstDim). - int srcDim = srcType & DIM_MASK; - if (srcDim != 0 && (srcType & KIND_MASK) != REFERENCE_KIND) { - srcDim = ELEMENT_OF + srcDim; - } - int dstDim = dstType & DIM_MASK; - if (dstDim != 0 && (dstType & KIND_MASK) != REFERENCE_KIND) { - dstDim = ELEMENT_OF + dstDim; - } - mergedType = - Math.min(srcDim, dstDim) | REFERENCE_KIND | symbolTable.addType("java/lang/Object"); - } else { - // If srcType is any other type, merge(srcType, dstType) = TOP. - mergedType = TOP; - } - } else if (dstType == NULL) { - // If dstType is the NULL type, merge(srcType, dstType) = srcType, or TOP if srcType is not a - // an array type or a reference type. - mergedType = - (srcType & DIM_MASK) != 0 || (srcType & KIND_MASK) == REFERENCE_KIND ? srcType : TOP; - } else { - // If dstType is any other type, merge(srcType, dstType) = TOP whatever srcType. - mergedType = TOP; - } - if (mergedType != dstType) { - dstTypes[dstIndex] = mergedType; - return true; - } - return false; - } - - // ----------------------------------------------------------------------------------------------- - // Frame output methods, to generate StackMapFrame attributes - // ----------------------------------------------------------------------------------------------- - - /** - * Makes the given {@link MethodWriter} visit the input frame of this {@link Frame}. The visit is - * done with the {@link MethodWriter#visitFrameStart}, {@link MethodWriter#visitAbstractType} and - * {@link MethodWriter#visitFrameEnd} methods. - * - * @param methodWriter the {@link MethodWriter} that should visit the input frame of this {@link - * Frame}. - */ - final void accept(final MethodWriter methodWriter) { - // Compute the number of locals, ignoring TOP types that are just after a LONG or a DOUBLE, and - // all trailing TOP types. - int[] localTypes = inputLocals; - int nLocal = 0; - int nTrailingTop = 0; - int i = 0; - while (i < localTypes.length) { - int localType = localTypes[i]; - i += (localType == LONG || localType == DOUBLE) ? 2 : 1; - if (localType == TOP) { - nTrailingTop++; - } else { - nLocal += nTrailingTop + 1; - nTrailingTop = 0; - } - } - // Compute the stack size, ignoring TOP types that are just after a LONG or a DOUBLE. - int[] stackTypes = inputStack; - int nStack = 0; - i = 0; - while (i < stackTypes.length) { - int stackType = stackTypes[i]; - i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1; - nStack++; - } - // Visit the frame and its content. - int frameIndex = methodWriter.visitFrameStart(owner.bytecodeOffset, nLocal, nStack); - i = 0; - while (nLocal-- > 0) { - int localType = localTypes[i]; - i += (localType == LONG || localType == DOUBLE) ? 2 : 1; - methodWriter.visitAbstractType(frameIndex++, localType); - } - i = 0; - while (nStack-- > 0) { - int stackType = stackTypes[i]; - i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1; - methodWriter.visitAbstractType(frameIndex++, stackType); - } - methodWriter.visitFrameEnd(); - } - - /** - * Put the given abstract type in the given ByteVector, using the JVMS verification_type_info - * format used in StackMapTable attributes. - * - * @param symbolTable the type table to use to lookup and store type {@link Symbol}. - * @param abstractType an abstract type, restricted to {@link Frame#CONSTANT_KIND}, {@link - * Frame#REFERENCE_KIND} or {@link Frame#UNINITIALIZED_KIND} types. - * @param output where the abstract type must be put. - * @see JVMS - * 4.7.4 - */ - static void putAbstractType( - final SymbolTable symbolTable, final int abstractType, final ByteVector output) { - int arrayDimensions = (abstractType & Frame.DIM_MASK) >> DIM_SHIFT; - if (arrayDimensions == 0) { - int typeValue = abstractType & VALUE_MASK; - switch (abstractType & KIND_MASK) { - case CONSTANT_KIND: - output.putByte(typeValue); - break; - case REFERENCE_KIND: - output - .putByte(ITEM_OBJECT) - .putShort(symbolTable.addConstantClass(symbolTable.getType(typeValue).value).index); - break; - case UNINITIALIZED_KIND: - output.putByte(ITEM_UNINITIALIZED).putShort((int) symbolTable.getType(typeValue).data); - break; - default: - throw new AssertionError(); - } - } else { - // Case of an array type, we need to build its descriptor first. - StringBuilder typeDescriptor = new StringBuilder(); - while (arrayDimensions-- > 0) { - typeDescriptor.append('['); - } - if ((abstractType & KIND_MASK) == REFERENCE_KIND) { - typeDescriptor - .append('L') - .append(symbolTable.getType(abstractType & VALUE_MASK).value) - .append(';'); - } else { - switch (abstractType & VALUE_MASK) { - case Frame.ITEM_ASM_BOOLEAN: - typeDescriptor.append('Z'); - break; - case Frame.ITEM_ASM_BYTE: - typeDescriptor.append('B'); - break; - case Frame.ITEM_ASM_CHAR: - typeDescriptor.append('C'); - break; - case Frame.ITEM_ASM_SHORT: - typeDescriptor.append('S'); - break; - case Frame.ITEM_INTEGER: - typeDescriptor.append('I'); - break; - case Frame.ITEM_FLOAT: - typeDescriptor.append('F'); - break; - case Frame.ITEM_LONG: - typeDescriptor.append('J'); - break; - case Frame.ITEM_DOUBLE: - typeDescriptor.append('D'); - break; - default: - throw new AssertionError(); - } - } - output - .putByte(ITEM_OBJECT) - .putShort(symbolTable.addConstantClass(typeDescriptor.toString()).index); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handle.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handle.java deleted file mode 100644 index f4737afe73..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handle.java +++ /dev/null @@ -1,189 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. - -package bsh.org.objectweb.asm; - -/** - * A reference to a field or a method. - * - * @author Remi Forax - * @author Eric Bruneton - */ -public final class Handle { - - /** - * The kind of field or method designated by this Handle. Should be {@link Opcodes#H_GETFIELD}, - * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link - * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. - */ - private final int tag; - - /** The internal name of the class that owns the field or method designated by this handle. */ - private final String owner; - - /** The name of the field or method designated by this handle. */ - private final String name; - - /** The descriptor of the field or method designated by this handle. */ - private final String descriptor; - - /** Whether the owner is an interface or not. */ - private final boolean isInterface; - - /** - * Constructs a new field or method handle. - * - * @param tag the kind of field or method designated by this Handle. Must be {@link - * Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link - * Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link - * Opcodes#H_INVOKEINTERFACE}. - * @param owner the internal name of the class that owns the field or method designated by this - * handle. - * @param name the name of the field or method designated by this handle. - * @param descriptor the descriptor of the field or method designated by this handle. - * @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String, - * boolean)}. - */ - @Deprecated - public Handle(final int tag, final String owner, final String name, final String descriptor) { - this(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE); - } - - /** - * Constructs a new field or method handle. - * - * @param tag the kind of field or method designated by this Handle. Must be {@link - * Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link - * Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link - * Opcodes#H_INVOKEINTERFACE}. - * @param owner the internal name of the class that owns the field or method designated by this - * handle. - * @param name the name of the field or method designated by this handle. - * @param descriptor the descriptor of the field or method designated by this handle. - * @param isInterface whether the owner is an interface or not. - */ - public Handle( - final int tag, - final String owner, - final String name, - final String descriptor, - final boolean isInterface) { - this.tag = tag; - this.owner = owner; - this.name = name; - this.descriptor = descriptor; - this.isInterface = isInterface; - } - - /** - * Returns the kind of field or method designated by this handle. - * - * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, - * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link - * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link - * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. - */ - public int getTag() { - return tag; - } - - /** - * Returns the internal name of the class that owns the field or method designated by this handle. - * - * @return the internal name of the class that owns the field or method designated by this handle. - */ - public String getOwner() { - return owner; - } - - /** - * Returns the name of the field or method designated by this handle. - * - * @return the name of the field or method designated by this handle. - */ - public String getName() { - return name; - } - - /** - * Returns the descriptor of the field or method designated by this handle. - * - * @return the descriptor of the field or method designated by this handle. - */ - public String getDesc() { - return descriptor; - } - - /** - * Returns true if the owner of the field or method designated by this handle is an interface. - * - * @return true if the owner of the field or method designated by this handle is an interface. - */ - public boolean isInterface() { - return isInterface; - } - - @Override - public boolean equals(final Object object) { - if (object == this) { - return true; - } - if (!(object instanceof Handle)) { - return false; - } - Handle handle = (Handle) object; - return tag == handle.tag - && isInterface == handle.isInterface - && owner.equals(handle.owner) - && name.equals(handle.name) - && descriptor.equals(handle.descriptor); - } - - @Override - public int hashCode() { - return tag - + (isInterface ? 64 : 0) - + owner.hashCode() * name.hashCode() * descriptor.hashCode(); - } - - /** - * Returns the textual representation of this handle. The textual representation is: - * - *

    - *
  • for a reference to a class: owner "." name descriptor " (" tag ")", - *
  • for a reference to an interface: owner "." name descriptor " (" tag " itf)". - *
- */ - @Override - public String toString() { - return owner + '.' + name + descriptor + " (" + tag + (isInterface ? " itf" : "") + ')'; - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handler.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handler.java deleted file mode 100644 index 43c1dcdcb1..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Handler.java +++ /dev/null @@ -1,198 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * Information about an exception handler. Corresponds to an element of the exception_table array of - * a Code attribute, as defined in the Java Virtual Machine Specification (JVMS). Handler instances - * can be chained together, with their {@link #nextHandler} field, to describe a full JVMS - * exception_table array. - * - * @see JVMS - * 4.7.3 - * @author Eric Bruneton - */ -final class Handler { - - /** - * The start_pc field of this JVMS exception_table entry. Corresponds to the beginning of the - * exception handler's scope (inclusive). - */ - final Label startPc; - - /** - * The end_pc field of this JVMS exception_table entry. Corresponds to the end of the exception - * handler's scope (exclusive). - */ - final Label endPc; - - /** - * The handler_pc field of this JVMS exception_table entry. Corresponding to the beginning of the - * exception handler's code. - */ - final Label handlerPc; - - /** - * The catch_type field of this JVMS exception_table entry. This is the constant pool index of the - * internal name of the type of exceptions handled by this handler, or 0 to catch any exceptions. - */ - final int catchType; - - /** - * The internal name of the type of exceptions handled by this handler, or null to catch - * any exceptions. - */ - final String catchTypeDescriptor; - - /** The next exception handler. */ - Handler nextHandler; - - /** - * Constructs a new Handler. - * - * @param startPc the start_pc field of this JVMS exception_table entry. - * @param endPc the end_pc field of this JVMS exception_table entry. - * @param handlerPc the handler_pc field of this JVMS exception_table entry. - * @param catchType The catch_type field of this JVMS exception_table entry. - * @param catchTypeDescriptor The internal name of the type of exceptions handled by this handler, - * or null to catch any exceptions. - */ - Handler( - final Label startPc, - final Label endPc, - final Label handlerPc, - final int catchType, - final String catchTypeDescriptor) { - this.startPc = startPc; - this.endPc = endPc; - this.handlerPc = handlerPc; - this.catchType = catchType; - this.catchTypeDescriptor = catchTypeDescriptor; - } - - /** - * Constructs a new Handler from the given one, with a different scope. - * - * @param handler an existing Handler. - * @param startPc the start_pc field of this JVMS exception_table entry. - * @param endPc the end_pc field of this JVMS exception_table entry. - */ - Handler(final Handler handler, final Label startPc, final Label endPc) { - this(startPc, endPc, handler.handlerPc, handler.catchType, handler.catchTypeDescriptor); - this.nextHandler = handler.nextHandler; - } - - /** - * Removes the range between start and end from the Handler list that begins with the given - * element. - * - * @param firstHandler the beginning of a Handler list. May be null. - * @param start the start of the range to be removed. - * @param end the end of the range to be removed. Maybe null. - * @return the exception handler list with the start-end range removed. - */ - static Handler removeRange(final Handler firstHandler, final Label start, final Label end) { - if (firstHandler == null) { - return null; - } else { - firstHandler.nextHandler = removeRange(firstHandler.nextHandler, start, end); - } - int handlerStart = firstHandler.startPc.bytecodeOffset; - int handlerEnd = firstHandler.endPc.bytecodeOffset; - int rangeStart = start.bytecodeOffset; - int rangeEnd = end == null ? Integer.MAX_VALUE : end.bytecodeOffset; - // Return early if [handlerStart,handlerEnd[ and [rangeStart,rangeEnd[ don't intersect. - if (rangeStart >= handlerEnd || rangeEnd <= handlerStart) { - return firstHandler; - } - if (rangeStart <= handlerStart) { - if (rangeEnd >= handlerEnd) { - // If [handlerStart,handlerEnd[ is included in [rangeStart,rangeEnd[, remove firstHandler. - return firstHandler.nextHandler; - } else { - // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [rangeEnd,handlerEnd[ - return new Handler(firstHandler, end, firstHandler.endPc); - } - } else if (rangeEnd >= handlerEnd) { - // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [handlerStart,rangeStart[ - return new Handler(firstHandler, firstHandler.startPc, start); - } else { - // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = - // [handlerStart,rangeStart[ + [rangeEnd,handerEnd[ - firstHandler.nextHandler = new Handler(firstHandler, end, firstHandler.endPc); - return new Handler(firstHandler, firstHandler.startPc, start); - } - } - - /** - * Returns the number of elements of the Handler list that begins with the given element. - * - * @param firstHandler the beginning of a Handler list. May be null. - * @return the number of elements of the Handler list that begins with 'handler'. - */ - static int getExceptionTableLength(final Handler firstHandler) { - int length = 0; - Handler handler = firstHandler; - while (handler != null) { - length++; - handler = handler.nextHandler; - } - return length; - } - - /** - * Returns the size in bytes of the JVMS exception_table corresponding to the Handler list that - * begins with the given element. This includes the exception_table_length field. - * - * @param firstHandler the beginning of a Handler list. May be null. - * @return the size in bytes of the exception_table_length and exception_table structures. - */ - static int getExceptionTableSize(final Handler firstHandler) { - return 2 + 8 * getExceptionTableLength(firstHandler); - } - - /** - * Puts the JVMS exception_table corresponding to the Handler list that begins with the given - * element. This includes the exception_table_length field. - * - * @param firstHandler the beginning of a Handler list. May be null. - * @param output where the exception_table_length and exception_table structures must be put. - */ - static void putExceptionTable(final Handler firstHandler, final ByteVector output) { - output.putShort(getExceptionTableLength(firstHandler)); - Handler handler = firstHandler; - while (handler != null) { - output - .putShort(handler.startPc.bytecodeOffset) - .putShort(handler.endPc.bytecodeOffset) - .putShort(handler.handlerPc.bytecodeOffset) - .putShort(handler.catchType); - handler = handler.nextHandler; - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/LICENSE.txt b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/LICENSE.txt deleted file mode 100644 index d9b93a0681..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/LICENSE.txt +++ /dev/null @@ -1,29 +0,0 @@ - ASM is released under the following 3-Clause BSD License: - - ASM: a very small and fast Java bytecode manipulation framework - Copyright (c) 2000-2011 INRIA, France Telecom - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Label.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Label.java deleted file mode 100644 index d2f805c475..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Label.java +++ /dev/null @@ -1,621 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A position in the bytecode of a method. Labels are used for jump, goto, and switch instructions, - * and for try catch blocks. A label designates the instruction that is just after. Note - * however that there can be other elements between a label and the instruction it designates (such - * as other labels, stack map frames, line numbers, etc.). - * - * @author Eric Bruneton - */ -public class Label { - - /** - * A flag indicating that a label is only used for debug attributes. Such a label is not the start - * of a basic block, the target of a jump instruction, or an exception handler. It can be safely - * ignored in control flow graph analysis algorithms (for optimization purposes). - */ - static final int FLAG_DEBUG_ONLY = 1; - - /** - * A flag indicating that a label is the target of a jump instruction, or the start of an - * exception handler. - */ - static final int FLAG_JUMP_TARGET = 2; - - /** A flag indicating that the bytecode offset of a label is known. */ - static final int FLAG_RESOLVED = 4; - - /** A flag indicating that a label corresponds to a reachable basic block. */ - static final int FLAG_REACHABLE = 8; - - /** - * A flag indicating that the basic block corresponding to a label ends with a subroutine call. By - * construction in {@link MethodWriter#visitJumpInsn}, labels with this flag set have at least two - * outgoing edges: - * - *
    - *
  • the first one corresponds to the instruction that follows the jsr instruction in the - * bytecode, i.e. where execution continues when it returns from the jsr call. This is a - * virtual control flow edge, since execution never goes directly from the jsr to the next - * instruction. Instead, it goes to the subroutine and eventually returns to the instruction - * following the jsr. This virtual edge is used to compute the real outgoing edges of the - * basic blocks ending with a ret instruction, in {@link #addSubroutineRetSuccessors}. - *
  • the second one corresponds to the target of the jsr instruction, - *
- */ - static final int FLAG_SUBROUTINE_CALLER = 16; - - /** - * A flag indicating that the basic block corresponding to a label is the start of a subroutine. - */ - static final int FLAG_SUBROUTINE_START = 32; - - /** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */ - static final int FLAG_SUBROUTINE_END = 64; - - /** - * The number of elements to add to the {@link #otherLineNumbers} array when it needs to be - * resized to store a new source line number. - */ - static final int LINE_NUMBERS_CAPACITY_INCREMENT = 4; - - /** - * The number of elements to add to the {@link #forwardReferences} array when it needs to be - * resized to store a new forward reference. - */ - static final int FORWARD_REFERENCES_CAPACITY_INCREMENT = 6; - - /** - * The bit mask to extract the type of a forward reference to this label. The extracted type is - * either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}. - * - * @see #forwardReferences - */ - static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000; - - /** - * The type of forward references stored with two bytes in the bytecode. This is the case, for - * instance, of a forward reference from an ifnull instruction. - */ - static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000; - - /** - * The type of forward references stored in four bytes in the bytecode. This is the case, for - * instance, of a forward reference from a lookupswitch instruction. - */ - static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000; - - /** - * The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle - * is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes, - * as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}). - * - * @see #forwardReferences - */ - static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF; - - /** - * A sentinel element used to indicate the end of a list of labels. - * - * @see #nextListElement - */ - static final Label EMPTY_LIST = new Label(); - - /** - * A user managed state associated with this label. Warning: this field is used by the ASM tree - * package. In order to use it with the ASM tree package you must override the getLabelNode method - * in MethodNode. - */ - public Object info; - - /** - * The type and status of this label or its corresponding basic block. Must be zero or more of - * {@link #FLAG_DEBUG_ONLY}, {@link #FLAG_JUMP_TARGET}, {@link #FLAG_RESOLVED}, {@link - * #FLAG_REACHABLE}, {@link #FLAG_SUBROUTINE_CALLER}, {@link #FLAG_SUBROUTINE_START}, {@link - * #FLAG_SUBROUTINE_END}. - */ - short flags; - - /** - * The source line number corresponding to this label, or 0. If there are several source line - * numbers corresponding to this label, the first one is stored in this field, and the remaining - * ones are stored in {@link #otherLineNumbers}. - */ - private short lineNumber; - - /** - * The source line numbers corresponding to this label, in addition to {@link #lineNumber}, or - * null. The first element of this array is the number n of source line numbers it contains, which - * are stored between indices 1 and n (inclusive). - */ - private int[] otherLineNumbers; - - /** - * The offset of this label in the bytecode of its method, in bytes. This value is set if and only - * if the {@link #FLAG_RESOLVED} flag is set. - */ - int bytecodeOffset; - - /** - * The forward references to this label. The first element is the number of forward references, - * times 2 (this corresponds to the index of the last element actually used in this array). Then, - * each forward reference is described with two consecutive integers noted - * 'sourceInsnBytecodeOffset' and 'reference': - * - *
    - *
  • 'sourceInsnBytecodeOffset' is the bytecode offset of the instruction that contains the - * forward reference, - *
  • 'reference' contains the type and the offset in the bytecode where the forward reference - * value must be stored, which can be extracted with {@link #FORWARD_REFERENCE_TYPE_MASK} - * and {@link #FORWARD_REFERENCE_HANDLE_MASK}. - *
- * - * For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is - * equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1 - * (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after - * the start of the instruction itself). For the default case of a lookupswitch instruction at - * bytecode offset x, 'sourceInsnBytecodeOffset' is equal to x, and 'reference' is of type {@link - * #FORWARD_REFERENCE_TYPE_WIDE} with value between x + 1 and x + 4 (because the lookupswitch - * instruction uses a 4 bytes bytecode offset operand stored one to four bytes after the start of - * the instruction itself). - */ - private int[] forwardReferences; - - // ----------------------------------------------------------------------------------------------- - - // Fields for the control flow and data flow graph analysis algorithms (used to compute the - // maximum stack size or the stack map frames). A control flow graph contains one node per "basic - // block", and one edge per "jump" from one basic block to another. Each node (i.e., each basic - // block) is represented with the Label object that corresponds to the first instruction of this - // basic block. Each node also stores the list of its successors in the graph, as a linked list of - // Edge objects. - // - // The control flow analysis algorithms used to compute the maximum stack size or the stack map - // frames are similar and use two steps. The first step, during the visit of each instruction, - // builds information about the state of the local variables and the operand stack at the end of - // each basic block, called the "output frame", relatively to the frame state at the - // beginning of the basic block, which is called the "input frame", and which is unknown - // during this step. The second step, in {@link MethodWriter#computeAllFrames} and {@link - // MethodWriter#computeMaxStackAndLocal}, is a fix point algorithm - // that computes information about the input frame of each basic block, from the input state of - // the first basic block (known from the method signature), and by the using the previously - // computed relative output frames. - // - // The algorithm used to compute the maximum stack size only computes the relative output and - // absolute input stack heights, while the algorithm used to compute stack map frames computes - // relative output frames and absolute input frames. - - /** - * The number of elements in the input stack of the basic block corresponding to this label. This - * field is computed in {@link MethodWriter#computeMaxStackAndLocal}. - */ - short inputStackSize; - - /** - * The number of elements in the output stack, at the end of the basic block corresponding to this - * label. This field is only computed for basic blocks that end with a RET instruction. - */ - short outputStackSize; - - /** - * The maximum height reached by the output stack, relatively to the top of the input stack, in - * the basic block corresponding to this label. This maximum is always positive or null. - */ - short outputStackMax; - - /** - * The id of the subroutine to which this basic block belongs, or 0. If the basic block belongs to - * several subroutines, this is the id of the "oldest" subroutine that contains it (with the - * convention that a subroutine calling another one is "older" than the callee). This field is - * computed in {@link MethodWriter#computeMaxStackAndLocal}, if the method contains JSR - * instructions. - */ - short subroutineId; - - /** - * The input and output stack map frames of the basic block corresponding to this label. This - * field is only used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} or {@link - * MethodWriter#COMPUTE_INSERTED_FRAMES} option is used. - */ - Frame frame; - - /** - * The successor of this label, in the order they are visited in {@link MethodVisitor#visitLabel}. - * This linked list does not include labels used for debug info only. If the {@link - * MethodWriter#COMPUTE_ALL_FRAMES} or {@link MethodWriter#COMPUTE_INSERTED_FRAMES} option is used - * then it does not contain either successive labels that denote the same bytecode offset (in this - * case only the first label appears in this list). - */ - Label nextBasicBlock; - - /** - * The outgoing edges of the basic block corresponding to this label, in the control flow graph of - * its method. These edges are stored in a linked list of {@link Edge} objects, linked to each - * other by their {@link Edge#nextEdge} field. - */ - Edge outgoingEdges; - - /** - * The next element in the list of labels to which this label belongs, or null if it does not - * belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in - * order to ensure that this field is null if and only if this label does not belong to a list of - * labels. Note that there can be several lists of labels at the same time, but that a label can - * belong to at most one list at a time (unless some lists share a common tail, but this is not - * used in practice). - * - *

List of labels are used in {@link MethodWriter#computeAllFrames} and {@link - * MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size, - * respectively, as well as in {@link #markSubroutine} and {@link #addSubroutineRetSuccessors} to - * compute the basic blocks belonging to subroutines and their outgoing edges. Outside of these - * methods, this field should be null (this property is a precondition and a postcondition of - * these methods). - */ - Label nextListElement; - - // ----------------------------------------------------------------------------------------------- - // Constructor and accessors - // ----------------------------------------------------------------------------------------------- - - /** Constructs a new label. */ - public Label() { - // Nothing to do. - } - - /** - * Returns the bytecode offset corresponding to this label. This offset is computed from the start - * of the method's bytecode. This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @return the bytecode offset corresponding to this label. - * @throws IllegalStateException if this label is not resolved yet. - */ - public int getOffset() { - if ((flags & FLAG_RESOLVED) == 0) { - throw new IllegalStateException("Label offset position has not been resolved yet"); - } - return bytecodeOffset; - } - - /** - * Returns the "canonical" {@link Label} instance corresponding to this label's bytecode offset, - * if known, otherwise the label itself. The canonical instance is the first label (in the order - * of their visit by {@link MethodVisitor#visitLabel}) corresponding to this bytecode offset. It - * cannot be known for labels which have not been visited yet. - * - *

This method should only be used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} option - * is used. - * - * @return the label itself if {@link #frame} is null, otherwise the Label's frame owner. This - * corresponds to the "canonical" label instance described above thanks to the way the label - * frame is set in {@link MethodWriter#visitLabel}. - */ - final Label getCanonicalInstance() { - return frame == null ? this : frame.owner; - } - - // ----------------------------------------------------------------------------------------------- - // Methods to manage line numbers - // ----------------------------------------------------------------------------------------------- - - /** - * Adds a source line number corresponding to this label. - * - * @param lineNumber a source line number (which should be strictly positive). - */ - final void addLineNumber(final int lineNumber) { - if (this.lineNumber == 0) { - this.lineNumber = (short) lineNumber; - } else { - if (otherLineNumbers == null) { - otherLineNumbers = new int[LINE_NUMBERS_CAPACITY_INCREMENT]; - } - int otherLineNumberIndex = ++otherLineNumbers[0]; - if (otherLineNumberIndex >= otherLineNumbers.length) { - int[] newLineNumbers = new int[otherLineNumbers.length + LINE_NUMBERS_CAPACITY_INCREMENT]; - System.arraycopy(otherLineNumbers, 0, newLineNumbers, 0, otherLineNumbers.length); - otherLineNumbers = newLineNumbers; - } - otherLineNumbers[otherLineNumberIndex] = lineNumber; - } - } - - /** - * Makes the given visitor visit this label and its source line numbers, if applicable. - * - * @param methodVisitor a method visitor. - * @param visitLineNumbers whether to visit of the label's source line numbers, if any. - */ - final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) { - methodVisitor.visitLabel(this); - if (visitLineNumbers && lineNumber != 0) { - methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this); - if (otherLineNumbers != null) { - for (int i = 1; i <= otherLineNumbers[0]; ++i) { - methodVisitor.visitLineNumber(otherLineNumbers[i], this); - } - } - } - } - - // ----------------------------------------------------------------------------------------------- - // Methods to compute offsets and to manage forward references - // ----------------------------------------------------------------------------------------------- - - /** - * Puts a reference to this label in the bytecode of a method. If the bytecode offset of the label - * is known, the relative bytecode offset between the label and the instruction referencing it is - * computed and written directly. Otherwise, a null relative offset is written and a new forward - * reference is declared for this label. - * - * @param code the bytecode of the method. This is where the reference is appended. - * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the - * reference to be appended. - * @param wideReference whether the reference must be stored in 4 bytes (instead of 2 bytes). - */ - final void put( - final ByteVector code, final int sourceInsnBytecodeOffset, final boolean wideReference) { - if ((flags & FLAG_RESOLVED) == 0) { - if (wideReference) { - addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_WIDE, code.length); - code.putInt(-1); - } else { - addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_SHORT, code.length); - code.putShort(-1); - } - } else { - if (wideReference) { - code.putInt(bytecodeOffset - sourceInsnBytecodeOffset); - } else { - code.putShort(bytecodeOffset - sourceInsnBytecodeOffset); - } - } - } - - /** - * Adds a forward reference to this label. This method must be called only for a true forward - * reference, i.e. only if this label is not resolved yet. For backward references, the relative - * bytecode offset of the reference can be, and must be, computed and stored directly. - * - * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the - * reference stored at referenceHandle. - * @param referenceType either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link - * #FORWARD_REFERENCE_TYPE_WIDE}. - * @param referenceHandle the offset in the bytecode where the forward reference value must be - * stored. - */ - private void addForwardReference( - final int sourceInsnBytecodeOffset, final int referenceType, final int referenceHandle) { - if (forwardReferences == null) { - forwardReferences = new int[FORWARD_REFERENCES_CAPACITY_INCREMENT]; - } - int lastElementIndex = forwardReferences[0]; - if (lastElementIndex + 2 >= forwardReferences.length) { - int[] newValues = new int[forwardReferences.length + FORWARD_REFERENCES_CAPACITY_INCREMENT]; - System.arraycopy(forwardReferences, 0, newValues, 0, forwardReferences.length); - forwardReferences = newValues; - } - forwardReferences[++lastElementIndex] = sourceInsnBytecodeOffset; - forwardReferences[++lastElementIndex] = referenceType | referenceHandle; - forwardReferences[0] = lastElementIndex; - } - - /** - * Sets the bytecode offset of this label to the given value and resolves the forward references - * to this label, if any. This method must be called when this label is added to the bytecode of - * the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that - * where left in the bytecode by each forward reference previously added to this label. - * - * @param code the bytecode of the method. - * @param bytecodeOffset the bytecode offset of this label. - * @return true if a blank that was left for this label was too small to store the - * offset. In such a case the corresponding jump instruction is replaced with an equivalent - * ASM specific instruction using an unsigned two bytes offset. These ASM specific - * instructions are later replaced with standard bytecode instructions with wider offsets (4 - * bytes instead of 2), in ClassReader. - */ - final boolean resolve(final byte[] code, final int bytecodeOffset) { - this.flags |= FLAG_RESOLVED; - this.bytecodeOffset = bytecodeOffset; - if (forwardReferences == null) { - return false; - } - boolean hasAsmInstructions = false; - for (int i = forwardReferences[0]; i > 0; i -= 2) { - final int sourceInsnBytecodeOffset = forwardReferences[i - 1]; - final int reference = forwardReferences[i]; - final int relativeOffset = bytecodeOffset - sourceInsnBytecodeOffset; - int handle = reference & FORWARD_REFERENCE_HANDLE_MASK; - if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) { - if (relativeOffset < Short.MIN_VALUE || relativeOffset > Short.MAX_VALUE) { - // Change the opcode of the jump instruction, in order to be able to find it later in - // ClassReader. These ASM specific opcodes are similar to jump instruction opcodes, except - // that the 2 bytes offset is unsigned (and can therefore represent values from 0 to - // 65535, which is sufficient since the size of a method is limited to 65535 bytes). - int opcode = code[sourceInsnBytecodeOffset] & 0xFF; - if (opcode < Opcodes.IFNULL) { - // Change IFEQ ... JSR to ASM_IFEQ ... ASM_JSR. - code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_OPCODE_DELTA); - } else { - // Change IFNULL and IFNONNULL to ASM_IFNULL and ASM_IFNONNULL. - code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_IFNULL_OPCODE_DELTA); - } - hasAsmInstructions = true; - } - code[handle++] = (byte) (relativeOffset >>> 8); - code[handle] = (byte) relativeOffset; - } else { - code[handle++] = (byte) (relativeOffset >>> 24); - code[handle++] = (byte) (relativeOffset >>> 16); - code[handle++] = (byte) (relativeOffset >>> 8); - code[handle] = (byte) relativeOffset; - } - } - return hasAsmInstructions; - } - - // ----------------------------------------------------------------------------------------------- - // Methods related to subroutines - // ----------------------------------------------------------------------------------------------- - - /** - * Finds the basic blocks that belong to the subroutine starting with the basic block - * corresponding to this label, and marks these blocks as belonging to this subroutine. This - * method follows the control flow graph to find all the blocks that are reachable from the - * current basic block WITHOUT following any jsr target. - * - *

Note: a precondition and postcondition of this method is that all labels must have a null - * {@link #nextListElement}. - * - * @param subroutineId the id of the subroutine starting with the basic block corresponding to - * this label. - */ - final void markSubroutine(final short subroutineId) { - // Data flow algorithm: put this basic block in a list of blocks to process (which are blocks - // belonging to subroutine subroutineId) and, while there are blocks to process, remove one from - // the list, mark it as belonging to the subroutine, and add its successor basic blocks in the - // control flow graph to the list of blocks to process (if not already done). - Label listOfBlocksToProcess = this; - listOfBlocksToProcess.nextListElement = EMPTY_LIST; - while (listOfBlocksToProcess != EMPTY_LIST) { - // Remove a basic block from the list of blocks to process. - Label basicBlock = listOfBlocksToProcess; - listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; - basicBlock.nextListElement = null; - - // If it is not already marked as belonging to a subroutine, mark it as belonging to - // subroutineId and add its successors to the list of blocks to process (unless already done). - if (basicBlock.subroutineId == 0) { - basicBlock.subroutineId = subroutineId; - listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess); - } - } - } - - /** - * Finds the basic blocks that end a subroutine starting with the basic block corresponding to - * this label and, for each one of them, adds an outgoing edge to the basic block following the - * given subroutine call. In other words, completes the control flow graph by adding the edges - * corresponding to the return from this subroutine, when called from the given caller basic - * block. - * - *

Note: a precondition and postcondition of this method is that all labels must have a null - * {@link #nextListElement}. - * - * @param subroutineCaller a basic block that ends with a jsr to the basic block corresponding to - * this label. This label is supposed to correspond to the start of a subroutine. - */ - final void addSubroutineRetSuccessors(final Label subroutineCaller) { - // Data flow algorithm: put this basic block in a list blocks to process (which are blocks - // belonging to a subroutine starting with this label) and, while there are blocks to process, - // remove one from the list, put it in a list of blocks that have been processed, add a return - // edge to the successor of subroutineCaller if applicable, and add its successor basic blocks - // in the control flow graph to the list of blocks to process (if not already done). - Label listOfProcessedBlocks = EMPTY_LIST; - Label listOfBlocksToProcess = this; - listOfBlocksToProcess.nextListElement = EMPTY_LIST; - while (listOfBlocksToProcess != EMPTY_LIST) { - // Move a basic block from the list of blocks to process to the list of processed blocks. - Label basicBlock = listOfBlocksToProcess; - listOfBlocksToProcess = basicBlock.nextListElement; - basicBlock.nextListElement = listOfProcessedBlocks; - listOfProcessedBlocks = basicBlock; - - // Add an edge from this block to the successor of the caller basic block, if this block is - // the end of a subroutine and if this block and subroutineCaller do not belong to the same - // subroutine. - if ((basicBlock.flags & FLAG_SUBROUTINE_END) != 0 - && basicBlock.subroutineId != subroutineCaller.subroutineId) { - basicBlock.outgoingEdges = - new Edge( - basicBlock.outputStackSize, - // By construction, the first outgoing edge of a basic block that ends with a jsr - // instruction leads to the jsr continuation block, i.e. where execution continues - // when ret is called (see {@link #FLAG_SUBROUTINE_CALLER}). - subroutineCaller.outgoingEdges.successor, - basicBlock.outgoingEdges); - } - // Add its successors to the list of blocks to process. Note that {@link #pushSuccessors} does - // not push basic blocks which are already in a list. Here this means either in the list of - // blocks to process, or in the list of already processed blocks. This second list is - // important to make sure we don't reprocess an already processed block. - listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess); - } - // Reset the {@link #nextListElement} of all the basic blocks that have been processed to null, - // so that this method can be called again with a different subroutine or subroutine caller. - while (listOfProcessedBlocks != EMPTY_LIST) { - Label newListOfProcessedBlocks = listOfProcessedBlocks.nextListElement; - listOfProcessedBlocks.nextListElement = null; - listOfProcessedBlocks = newListOfProcessedBlocks; - } - } - - /** - * Adds the successors of this label in the method's control flow graph (except those - * corresponding to a jsr target, and those already in a list of labels) to the given list of - * blocks to process, and returns the new list. - * - * @param listOfLabelsToProcess a list of basic blocks to process, linked together with their - * {@link #nextListElement} field. - * @return the new list of blocks to process. - */ - private Label pushSuccessors(final Label listOfLabelsToProcess) { - Label newListOfLabelsToProcess = listOfLabelsToProcess; - Edge outgoingEdge = outgoingEdges; - while (outgoingEdge != null) { - // By construction, the second outgoing edge of a basic block that ends with a jsr instruction - // leads to the jsr target (see {@link #FLAG_SUBROUTINE_CALLER}). - boolean isJsrTarget = - (flags & Label.FLAG_SUBROUTINE_CALLER) != 0 && outgoingEdge == outgoingEdges.nextEdge; - if (!isJsrTarget && outgoingEdge.successor.nextListElement == null) { - // Add this successor to the list of blocks to process, if it does not already belong to a - // list of labels. - outgoingEdge.successor.nextListElement = newListOfLabelsToProcess; - newListOfLabelsToProcess = outgoingEdge.successor; - } - outgoingEdge = outgoingEdge.nextEdge; - } - return newListOfLabelsToProcess; - } - - // ----------------------------------------------------------------------------------------------- - // Overridden Object methods - // ----------------------------------------------------------------------------------------------- - - /** - * Returns a string representation of this label. - * - * @return a string representation of this label. - */ - @Override - public String toString() { - return "L" + System.identityHashCode(this); - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodVisitor.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodVisitor.java deleted file mode 100644 index e493afcbd0..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodVisitor.java +++ /dev/null @@ -1,609 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A visitor to visit a Java method. The methods of this class must be called in the following - * order: ( visitParameter )* [ visitAnnotationDefault ] ( - * visitAnnotation | visitAnnotableParameterCount | - * visitParameterAnnotation visitTypeAnnotation | visitAttribute )* [ - * visitCode ( visitFrame | visitXInsn | visitLabel | - * visitInsnAnnotation | visitTryCatchBlock | visitTryCatchAnnotation | - * visitLocalVariable | visitLocalVariableAnnotation | visitLineNumber )* - * visitMaxs ] visitEnd. In addition, the visitXInsn and - * visitLabel methods must be called in the sequential order of the bytecode instructions - * of the visited code, visitInsnAnnotation must be called after the annotated - * instruction, visitTryCatchBlock must be called before the labels passed as - * arguments have been visited, visitTryCatchBlockAnnotation must be called after - * the corresponding try catch block has been visited, and the visitLocalVariable, - * visitLocalVariableAnnotation and visitLineNumber methods must be called - * after the labels passed as arguments have been visited. - * - * @author Eric Bruneton - */ -public abstract class MethodVisitor { - - private static final String REQUIRES_ASM5 = "This feature requires ASM5"; - - /** - * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7_EXPERIMENTAL}. - */ - protected final int api; - - /** The method visitor to which this visitor must delegate method calls. May be null. */ - protected MethodVisitor mv; - - /** - * Constructs a new {@link MethodVisitor}. - * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link - * Opcodes#ASM7_EXPERIMENTAL}. - */ - public MethodVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link MethodVisitor}. - * - * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link - * Opcodes#ASM7_EXPERIMENTAL}. - * @param methodVisitor the method visitor to which this visitor must delegate method calls. May - * be null. - */ - public MethodVisitor(final int api, final MethodVisitor methodVisitor) { - if (api != Opcodes.ASM6 - && api != Opcodes.ASM5 - && api != Opcodes.ASM4) { - throw new IllegalArgumentException(); - } - this.api = api; - this.mv = methodVisitor; - } - - // ----------------------------------------------------------------------------------------------- - // Parameters, annotations and non standard attributes - // ----------------------------------------------------------------------------------------------- - - /** - * Visits a parameter of this method. - * - * @param name parameter name or null if none is provided. - * @param access the parameter's access flags, only ACC_FINAL, ACC_SYNTHETIC - * or/and ACC_MANDATED are allowed (see {@link Opcodes}). - */ - public void visitParameter(final String name, final int access) { - if (api < Opcodes.ASM5) { - throw new UnsupportedOperationException(REQUIRES_ASM5); - } - if (mv != null) { - mv.visitParameter(name, access); - } - } - - /** - * Visits the number of method parameters that can have annotations. By default (i.e. when this - * method is not called), all the method parameters defined by the method descriptor can have - * annotations. - * - * @param parameterCount the number of method parameters than can have annotations. This number - * must be less or equal than the number of parameter types in the method descriptor. It can - * be strictly less when a method has synthetic parameters and when these parameters are - * ignored when computing parameter indices for the purpose of parameter annotations (see - * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18). - * @param visible true to define the number of method parameters that can have - * annotations visible at runtime, false to define the number of method parameters - * that can have annotations invisible at runtime. - */ - public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { - if (mv != null) { - mv.visitAnnotableParameterCount(parameterCount, visible); - } - } - - /** - * Visits a non standard attribute of this method. - * - * @param attribute an attribute. - */ - public void visitAttribute(final Attribute attribute) { - if (mv != null) { - mv.visitAttribute(attribute); - } - } - - /** Starts the visit of the method's code, if any (i.e. non abstract method). */ - public void visitCode() { - if (mv != null) { - mv.visitCode(); - } - } - - /** - * Visits the current state of the local variables and operand stack elements. This method must(*) - * be called just before any instruction i that follows an unconditional branch - * instruction such as GOTO or THROW, that is the target of a jump instruction, or that starts an - * exception handler block. The visited types must describe the values of the local variables and - * of the operand stack elements just before i is executed.
- *
- * (*) this is mandatory only for classes whose version is greater than or equal to {@link - * Opcodes#V1_6}.
- *
- * The frames of a method must be given either in expanded form, or in compressed form (all frames - * must use the same format, i.e. you must not mix expanded and compressed frames within a single - * method): - * - *

    - *
  • In expanded form, all frames must have the F_NEW type. - *
  • In compressed form, frames are basically "deltas" from the state of the previous frame: - *
      - *
    • {@link Opcodes#F_SAME} representing frame with exactly the same locals as the - * previous frame and with the empty stack. - *
    • {@link Opcodes#F_SAME1} representing frame with exactly the same locals as the - * previous frame and with single value on the stack ( nStack is 1 and - * stack[0] contains value for the type of the stack item). - *
    • {@link Opcodes#F_APPEND} representing frame with current locals are the same as the - * locals in the previous frame, except that additional locals are defined ( - * nLocal is 1, 2 or 3 and local elements contains values - * representing added types). - *
    • {@link Opcodes#F_CHOP} representing frame with current locals are the same as the - * locals in the previous frame, except that the last 1-3 locals are absent and with - * the empty stack (nLocals is 1, 2 or 3). - *
    • {@link Opcodes#F_FULL} representing complete frame data. - *
    - *
- * - *
- * In both cases the first frame, corresponding to the method's parameters and access flags, is - * implicit and must not be visited. Also, it is illegal to visit two or more frames for the same - * code location (i.e., at least one instruction must be visited between two calls to visitFrame). - * - * @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded - * frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link - * Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames. - * @param nLocal the number of local variables in the visited frame. - * @param local the local variable types in this frame. This array must not be modified. Primitive - * types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link - * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a single element). - * Reference types are represented by String objects (representing internal names), and - * uninitialized types by Label objects (this label designates the NEW instruction that - * created this uninitialized value). - * @param nStack the number of operand stack elements in the visited frame. - * @param stack the operand stack types in this frame. This array must not be modified. Its - * content has the same format as the "local" array. - * @throws IllegalStateException if a frame is visited just after another one, without any - * instruction between the two (unless this frame is a Opcodes#F_SAME frame, in which case it - * is silently ignored). - */ - public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack) { - if (mv != null) { - mv.visitFrame(type, nLocal, local, nStack, stack); - } - } - - // ----------------------------------------------------------------------------------------------- - // Normal instructions - // ----------------------------------------------------------------------------------------------- - - /** - * Visits a zero operand instruction. - * - * @param opcode the opcode of the instruction to be visited. This opcode is either NOP, - * ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, - * LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, - * FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, DASTORE, - * AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, - * SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, - * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, - * LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, - * D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, - * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT. - */ - public void visitInsn(final int opcode) { - if (mv != null) { - mv.visitInsn(opcode); - } - } - - /** - * Visits an instruction with a single int operand. - * - * @param opcode the opcode of the instruction to be visited. This opcode is either BIPUSH, SIPUSH - * or NEWARRAY. - * @param operand the operand of the instruction to be visited.
- * When opcode is BIPUSH, operand value should be between Byte.MIN_VALUE and Byte.MAX_VALUE. - *
- * When opcode is SIPUSH, operand value should be between Short.MIN_VALUE and Short.MAX_VALUE. - *
- * When opcode is NEWARRAY, operand value should be one of {@link Opcodes#T_BOOLEAN}, {@link - * Opcodes#T_CHAR}, {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, {@link Opcodes#T_BYTE}, - * {@link Opcodes#T_SHORT}, {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. - */ - public void visitIntInsn(final int opcode, final int operand) { - if (mv != null) { - mv.visitIntInsn(opcode, operand); - } - } - - /** - * Visits a local variable instruction. A local variable instruction is an instruction that loads - * or stores the value of a local variable. - * - * @param opcode the opcode of the local variable instruction to be visited. This opcode is either - * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. - * @param var the operand of the instruction to be visited. This operand is the index of a local - * variable. - */ - public void visitVarInsn(final int opcode, final int var) { - if (mv != null) { - mv.visitVarInsn(opcode, var); - } - } - - /** - * Visits a type instruction. A type instruction is an instruction that takes the internal name of - * a class as parameter. - * - * @param opcode the opcode of the type instruction to be visited. This opcode is either NEW, - * ANEWARRAY, CHECKCAST or INSTANCEOF. - * @param type the operand of the instruction to be visited. This operand must be the internal - * name of an object or array class (see {@link Type#getInternalName()}). - */ - public void visitTypeInsn(final int opcode, final String type) { - if (mv != null) { - mv.visitTypeInsn(opcode, type); - } - } - - /** - * Visits a field instruction. A field instruction is an instruction that loads or stores the - * value of a field of an object. - * - * @param opcode the opcode of the type instruction to be visited. This opcode is either - * GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. - * @param owner the internal name of the field's owner class (see {@link Type#getInternalName()}). - * @param name the field's name. - * @param descriptor the field's descriptor (see {@link Type}). - */ - public void visitFieldInsn( - final int opcode, final String owner, final String name, final String descriptor) { - if (mv != null) { - mv.visitFieldInsn(opcode, owner, name, descriptor); - } - } - - /** - * Visits a method instruction. A method instruction is an instruction that invokes a method. - * - * @param opcode the opcode of the type instruction to be visited. This opcode is either - * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. - * @param owner the internal name of the method's owner class (see {@link - * Type#getInternalName()}). - * @param name the method's name. - * @param descriptor the method's descriptor (see {@link Type}). - * @deprecated - */ - @Deprecated - public void visitMethodInsn( - final int opcode, final String owner, final String name, final String descriptor) { - if (api >= Opcodes.ASM5) { - boolean isInterface = opcode == Opcodes.INVOKEINTERFACE; - visitMethodInsn(opcode, owner, name, descriptor, isInterface); - return; - } - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, descriptor); - } - } - - /** - * Visits a method instruction. A method instruction is an instruction that invokes a method. - * - * @param opcode the opcode of the type instruction to be visited. This opcode is either - * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. - * @param owner the internal name of the method's owner class (see {@link - * Type#getInternalName()}). - * @param name the method's name. - * @param descriptor the method's descriptor (see {@link Type}). - * @param isInterface if the method's owner class is an interface. - */ - public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String descriptor, - final boolean isInterface) { - if (api < Opcodes.ASM5) { - if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) { - throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5"); - } - visitMethodInsn(opcode, owner, name, descriptor); - return; - } - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); - } - } - - /** - * Visits an invokedynamic instruction. - * - * @param name the method's name. - * @param descriptor the method's descriptor (see {@link Type}). - * @param bootstrapMethodHandle the bootstrap method. - * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be - * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link - * Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify - * the content of the array so a caller should expect that this array may change. - */ - public void visitInvokeDynamicInsn( - final String name, - final String descriptor, - final Handle bootstrapMethodHandle, - final Object... bootstrapMethodArguments) { - if (api < Opcodes.ASM5) { - throw new UnsupportedOperationException(REQUIRES_ASM5); - } - if (mv != null) { - mv.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); - } - } - - /** - * Visits a jump instruction. A jump instruction is an instruction that may jump to another - * instruction. - * - * @param opcode the opcode of the type instruction to be visited. This opcode is either IFEQ, - * IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, - * IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. - * @param label the operand of the instruction to be visited. This operand is a label that - * designates the instruction to which the jump instruction may jump. - */ - public void visitJumpInsn(final int opcode, final Label label) { - if (mv != null) { - mv.visitJumpInsn(opcode, label); - } - } - - /** - * Visits a label. A label designates the instruction that will be visited just after it. - * - * @param label a {@link Label} object. - */ - public void visitLabel(final Label label) { - if (mv != null) { - mv.visitLabel(label); - } - } - - // ----------------------------------------------------------------------------------------------- - // Special instructions - // ----------------------------------------------------------------------------------------------- - - /** - * Visits a LDC instruction. Note that new constant types may be added in future versions of the - * Java Virtual Machine. To easily detect new constant types, implementations of this method - * should check for unexpected constant types, like this: - * - *
-   * if (cst instanceof Integer) {
-   *     // ...
-   * } else if (cst instanceof Float) {
-   *     // ...
-   * } else if (cst instanceof Long) {
-   *     // ...
-   * } else if (cst instanceof Double) {
-   *     // ...
-   * } else if (cst instanceof String) {
-   *     // ...
-   * } else if (cst instanceof Type) {
-   *     int sort = ((Type) cst).getSort();
-   *     if (sort == Type.OBJECT) {
-   *         // ...
-   *     } else if (sort == Type.ARRAY) {
-   *         // ...
-   *     } else if (sort == Type.METHOD) {
-   *         // ...
-   *     } else {
-   *         // throw an exception
-   *     }
-   * } else if (cst instanceof Handle) {
-   *     // ...
-   * } else if (cst instanceof Condy) {
-   *     // ...
-   * } else {
-   *     // throw an exception
-   * }
-   * 
- * - * @param value the constant to be loaded on the stack. This parameter must be a non null {@link - * Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, a {@link - * Type} of OBJECT or ARRAY sort for .class constants, for classes whose version is - * 49, a {@link Type} of METHOD sort for MethodType, a {@link Handle} for MethodHandle - * constants, for classes whose version is 51 or a {@link ConstantDynamic} for a constant - * dynamic for classes whose version is 55. - */ - public void visitLdcInsn(final Object value) { - if (api < Opcodes.ASM5 - && (value instanceof Handle - || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) { - throw new UnsupportedOperationException(REQUIRES_ASM5); - } - if (mv != null) { - mv.visitLdcInsn(value); - } - } - - /** - * Visits an IINC instruction. - * - * @param var index of the local variable to be incremented. - * @param increment amount to increment the local variable by. - */ - public void visitIincInsn(final int var, final int increment) { - if (mv != null) { - mv.visitIincInsn(var, increment); - } - } - - /** - * Visits a TABLESWITCH instruction. - * - * @param min the minimum key value. - * @param max the maximum key value. - * @param dflt beginning of the default handler block. - * @param labels beginnings of the handler blocks. labels[i] is the beginning of the - * handler block for the min + i key. - */ - public void visitTableSwitchInsn( - final int min, final int max, final Label dflt, final Label... labels) { - if (mv != null) { - mv.visitTableSwitchInsn(min, max, dflt, labels); - } - } - - /** - * Visits a LOOKUPSWITCH instruction. - * - * @param dflt beginning of the default handler block. - * @param keys the values of the keys. - * @param labels beginnings of the handler blocks. labels[i] is the beginning of the - * handler block for the keys[i] key. - */ - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { - if (mv != null) { - mv.visitLookupSwitchInsn(dflt, keys, labels); - } - } - - /** - * Visits a MULTIANEWARRAY instruction. - * - * @param descriptor an array type descriptor (see {@link Type}). - * @param numDimensions the number of dimensions of the array to allocate. - */ - public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { - if (mv != null) { - mv.visitMultiANewArrayInsn(descriptor, numDimensions); - } - } - - // ----------------------------------------------------------------------------------------------- - // Exceptions table entries, debug information, max stack and max locals - // ----------------------------------------------------------------------------------------------- - - /** - * Visits a try catch block. - * - * @param start the beginning of the exception handler's scope (inclusive). - * @param end the end of the exception handler's scope (exclusive). - * @param handler the beginning of the exception handler's code. - * @param type the internal name of the type of exceptions handled by the handler, or - * null to catch any exceptions (for "finally" blocks). - * @throws IllegalArgumentException if one of the labels has already been visited by this visitor - * (by the {@link #visitLabel} method). - */ - public void visitTryCatchBlock( - final Label start, final Label end, final Label handler, final String type) { - if (mv != null) { - mv.visitTryCatchBlock(start, end, handler, type); - } - } - - /** - * Visits a local variable declaration. - * - * @param name the name of a local variable. - * @param descriptor the type descriptor of this local variable. - * @param signature the type signature of this local variable. May be null if the local - * variable type does not use generic types. - * @param start the first instruction corresponding to the scope of this local variable - * (inclusive). - * @param end the last instruction corresponding to the scope of this local variable (exclusive). - * @param index the local variable's index. - * @throws IllegalArgumentException if one of the labels has not already been visited by this - * visitor (by the {@link #visitLabel} method). - */ - public void visitLocalVariable( - final String name, - final String descriptor, - final String signature, - final Label start, - final Label end, - final int index) { - if (mv != null) { - mv.visitLocalVariable(name, descriptor, signature, start, end, index); - } - } - - /** - * Visits a line number declaration. - * - * @param line a line number. This number refers to the source file from which the class was - * compiled. - * @param start the first instruction corresponding to this line number. - * @throws IllegalArgumentException if start has not already been visited by this visitor - * (by the {@link #visitLabel} method). - */ - public void visitLineNumber(final int line, final Label start) { - if (mv != null) { - mv.visitLineNumber(line, start); - } - } - - /** - * Visits the maximum stack size and the maximum number of local variables of the method. - * - * @param maxStack maximum stack size of the method. - * @param maxLocals maximum number of local variables for the method. - */ - public void visitMaxs(final int maxStack, final int maxLocals) { - if (mv != null) { - mv.visitMaxs(maxStack, maxLocals); - } - } - - /** - * Visits the end of the method. This method, which is the last one to be called, is used to - * inform the visitor that all the annotations and attributes of the method have been visited. - */ - public void visitEnd() { - if (mv != null) { - mv.visitEnd(); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodWriter.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodWriter.java deleted file mode 100644 index e7afa93e87..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/MethodWriter.java +++ /dev/null @@ -1,2068 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * A {@link MethodVisitor} that generates a corresponding 'method_info' structure, as defined in the - * Java Virtual Machine Specification (JVMS). - * - * @see JVMS - * 4.6 - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -final class MethodWriter extends MethodVisitor { - - /** Indicates that nothing must be computed. */ - static final int COMPUTE_NOTHING = 0; - - /** - * Indicates that the maximum stack size and the maximum number of local variables must be - * computed, from scratch. - */ - static final int COMPUTE_MAX_STACK_AND_LOCAL = 1; - - /** - * Indicates that the maximum stack size and the maximum number of local variables must be - * computed, from the existing stack map frames. This can be done more efficiently than with the - * control flow graph algorithm used for {@link #COMPUTE_MAX_STACK_AND_LOCAL}, by using a linear - * scan of the bytecode instructions. - */ - static final int COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES = 2; - - /** - * Indicates that the stack map frames of type F_INSERT must be computed. The other frames are not - * computed. They should all be of type F_NEW and should be sufficient to compute the content of - * the F_INSERT frames, together with the bytecode instructions between a F_NEW and a F_INSERT - * frame - and without any knowledge of the type hierarchy (by definition of F_INSERT). - */ - static final int COMPUTE_INSERTED_FRAMES = 3; - - /** - * Indicates that all the stack map frames must be computed. In this case the maximum stack size - * and the maximum number of local variables is also computed. - */ - static final int COMPUTE_ALL_FRAMES = 4; - - /** Indicates that {@link #STACK_SIZE_DELTA} is not applicable (not constant or never used). */ - private static final int NA = 0; - - /** - * The stack size variation corresponding to each JVM opcode. The stack size variation for opcode - * 'o' is given by the array element at index 'o'. - * - * @see JVMS 6 - */ - private static final int[] STACK_SIZE_DELTA = { - 0, // nop = 0 (0x0) - 1, // aconst_null = 1 (0x1) - 1, // iconst_m1 = 2 (0x2) - 1, // iconst_0 = 3 (0x3) - 1, // iconst_1 = 4 (0x4) - 1, // iconst_2 = 5 (0x5) - 1, // iconst_3 = 6 (0x6) - 1, // iconst_4 = 7 (0x7) - 1, // iconst_5 = 8 (0x8) - 2, // lconst_0 = 9 (0x9) - 2, // lconst_1 = 10 (0xa) - 1, // fconst_0 = 11 (0xb) - 1, // fconst_1 = 12 (0xc) - 1, // fconst_2 = 13 (0xd) - 2, // dconst_0 = 14 (0xe) - 2, // dconst_1 = 15 (0xf) - 1, // bipush = 16 (0x10) - 1, // sipush = 17 (0x11) - 1, // ldc = 18 (0x12) - NA, // ldc_w = 19 (0x13) - NA, // ldc2_w = 20 (0x14) - 1, // iload = 21 (0x15) - 2, // lload = 22 (0x16) - 1, // fload = 23 (0x17) - 2, // dload = 24 (0x18) - 1, // aload = 25 (0x19) - NA, // iload_0 = 26 (0x1a) - NA, // iload_1 = 27 (0x1b) - NA, // iload_2 = 28 (0x1c) - NA, // iload_3 = 29 (0x1d) - NA, // lload_0 = 30 (0x1e) - NA, // lload_1 = 31 (0x1f) - NA, // lload_2 = 32 (0x20) - NA, // lload_3 = 33 (0x21) - NA, // fload_0 = 34 (0x22) - NA, // fload_1 = 35 (0x23) - NA, // fload_2 = 36 (0x24) - NA, // fload_3 = 37 (0x25) - NA, // dload_0 = 38 (0x26) - NA, // dload_1 = 39 (0x27) - NA, // dload_2 = 40 (0x28) - NA, // dload_3 = 41 (0x29) - NA, // aload_0 = 42 (0x2a) - NA, // aload_1 = 43 (0x2b) - NA, // aload_2 = 44 (0x2c) - NA, // aload_3 = 45 (0x2d) - -1, // iaload = 46 (0x2e) - 0, // laload = 47 (0x2f) - -1, // faload = 48 (0x30) - 0, // daload = 49 (0x31) - -1, // aaload = 50 (0x32) - -1, // baload = 51 (0x33) - -1, // caload = 52 (0x34) - -1, // saload = 53 (0x35) - -1, // istore = 54 (0x36) - -2, // lstore = 55 (0x37) - -1, // fstore = 56 (0x38) - -2, // dstore = 57 (0x39) - -1, // astore = 58 (0x3a) - NA, // istore_0 = 59 (0x3b) - NA, // istore_1 = 60 (0x3c) - NA, // istore_2 = 61 (0x3d) - NA, // istore_3 = 62 (0x3e) - NA, // lstore_0 = 63 (0x3f) - NA, // lstore_1 = 64 (0x40) - NA, // lstore_2 = 65 (0x41) - NA, // lstore_3 = 66 (0x42) - NA, // fstore_0 = 67 (0x43) - NA, // fstore_1 = 68 (0x44) - NA, // fstore_2 = 69 (0x45) - NA, // fstore_3 = 70 (0x46) - NA, // dstore_0 = 71 (0x47) - NA, // dstore_1 = 72 (0x48) - NA, // dstore_2 = 73 (0x49) - NA, // dstore_3 = 74 (0x4a) - NA, // astore_0 = 75 (0x4b) - NA, // astore_1 = 76 (0x4c) - NA, // astore_2 = 77 (0x4d) - NA, // astore_3 = 78 (0x4e) - -3, // iastore = 79 (0x4f) - -4, // lastore = 80 (0x50) - -3, // fastore = 81 (0x51) - -4, // dastore = 82 (0x52) - -3, // aastore = 83 (0x53) - -3, // bastore = 84 (0x54) - -3, // castore = 85 (0x55) - -3, // sastore = 86 (0x56) - -1, // pop = 87 (0x57) - -2, // pop2 = 88 (0x58) - 1, // dup = 89 (0x59) - 1, // dup_x1 = 90 (0x5a) - 1, // dup_x2 = 91 (0x5b) - 2, // dup2 = 92 (0x5c) - 2, // dup2_x1 = 93 (0x5d) - 2, // dup2_x2 = 94 (0x5e) - 0, // swap = 95 (0x5f) - -1, // iadd = 96 (0x60) - -2, // ladd = 97 (0x61) - -1, // fadd = 98 (0x62) - -2, // dadd = 99 (0x63) - -1, // isub = 100 (0x64) - -2, // lsub = 101 (0x65) - -1, // fsub = 102 (0x66) - -2, // dsub = 103 (0x67) - -1, // imul = 104 (0x68) - -2, // lmul = 105 (0x69) - -1, // fmul = 106 (0x6a) - -2, // dmul = 107 (0x6b) - -1, // idiv = 108 (0x6c) - -2, // ldiv = 109 (0x6d) - -1, // fdiv = 110 (0x6e) - -2, // ddiv = 111 (0x6f) - -1, // irem = 112 (0x70) - -2, // lrem = 113 (0x71) - -1, // frem = 114 (0x72) - -2, // drem = 115 (0x73) - 0, // ineg = 116 (0x74) - 0, // lneg = 117 (0x75) - 0, // fneg = 118 (0x76) - 0, // dneg = 119 (0x77) - -1, // ishl = 120 (0x78) - -1, // lshl = 121 (0x79) - -1, // ishr = 122 (0x7a) - -1, // lshr = 123 (0x7b) - -1, // iushr = 124 (0x7c) - -1, // lushr = 125 (0x7d) - -1, // iand = 126 (0x7e) - -2, // land = 127 (0x7f) - -1, // ior = 128 (0x80) - -2, // lor = 129 (0x81) - -1, // ixor = 130 (0x82) - -2, // lxor = 131 (0x83) - 0, // iinc = 132 (0x84) - 1, // i2l = 133 (0x85) - 0, // i2f = 134 (0x86) - 1, // i2d = 135 (0x87) - -1, // l2i = 136 (0x88) - -1, // l2f = 137 (0x89) - 0, // l2d = 138 (0x8a) - 0, // f2i = 139 (0x8b) - 1, // f2l = 140 (0x8c) - 1, // f2d = 141 (0x8d) - -1, // d2i = 142 (0x8e) - 0, // d2l = 143 (0x8f) - -1, // d2f = 144 (0x90) - 0, // i2b = 145 (0x91) - 0, // i2c = 146 (0x92) - 0, // i2s = 147 (0x93) - -3, // lcmp = 148 (0x94) - -1, // fcmpl = 149 (0x95) - -1, // fcmpg = 150 (0x96) - -3, // dcmpl = 151 (0x97) - -3, // dcmpg = 152 (0x98) - -1, // ifeq = 153 (0x99) - -1, // ifne = 154 (0x9a) - -1, // iflt = 155 (0x9b) - -1, // ifge = 156 (0x9c) - -1, // ifgt = 157 (0x9d) - -1, // ifle = 158 (0x9e) - -2, // if_icmpeq = 159 (0x9f) - -2, // if_icmpne = 160 (0xa0) - -2, // if_icmplt = 161 (0xa1) - -2, // if_icmpge = 162 (0xa2) - -2, // if_icmpgt = 163 (0xa3) - -2, // if_icmple = 164 (0xa4) - -2, // if_acmpeq = 165 (0xa5) - -2, // if_acmpne = 166 (0xa6) - 0, // goto = 167 (0xa7) - 1, // jsr = 168 (0xa8) - 0, // ret = 169 (0xa9) - -1, // tableswitch = 170 (0xaa) - -1, // lookupswitch = 171 (0xab) - -1, // ireturn = 172 (0xac) - -2, // lreturn = 173 (0xad) - -1, // freturn = 174 (0xae) - -2, // dreturn = 175 (0xaf) - -1, // areturn = 176 (0xb0) - 0, // return = 177 (0xb1) - NA, // getstatic = 178 (0xb2) - NA, // putstatic = 179 (0xb3) - NA, // getfield = 180 (0xb4) - NA, // putfield = 181 (0xb5) - NA, // invokevirtual = 182 (0xb6) - NA, // invokespecial = 183 (0xb7) - NA, // invokestatic = 184 (0xb8) - NA, // invokeinterface = 185 (0xb9) - NA, // invokedynamic = 186 (0xba) - 1, // new = 187 (0xbb) - 0, // newarray = 188 (0xbc) - 0, // anewarray = 189 (0xbd) - 0, // arraylength = 190 (0xbe) - NA, // athrow = 191 (0xbf) - 0, // checkcast = 192 (0xc0) - 0, // instanceof = 193 (0xc1) - -1, // monitorenter = 194 (0xc2) - -1, // monitorexit = 195 (0xc3) - NA, // wide = 196 (0xc4) - NA, // multianewarray = 197 (0xc5) - -1, // ifnull = 198 (0xc6) - -1, // ifnonnull = 199 (0xc7) - NA, // goto_w = 200 (0xc8) - NA // jsr_w = 201 (0xc9) - }; - - /** Where the constants used in this MethodWriter must be stored. */ - private final SymbolTable symbolTable; - - // Note: fields are ordered as in the method_info structure, and those related to attributes are - // ordered as in Section 4.7 of the JVMS. - - /** - * The access_flags field of the method_info JVMS structure. This field can contain ASM specific - * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the - * ClassFile structure. - */ - private final int accessFlags; - - /** The name_index field of the method_info JVMS structure. */ - private final int nameIndex; - - /** The descriptor_index field of the method_info JVMS structure. */ - private final int descriptorIndex; - - /** The descriptor of this method. */ - private final String descriptor; - - // Code attribute fields and sub attributes: - - /** The max_stack field of the Code attribute. */ - private int maxStack; - - /** The max_locals field of the Code attribute. */ - private int maxLocals; - - /** The 'code' field of the Code attribute. */ - private final ByteVector code = new ByteVector(); - - /** - * The first element in the exception handler list (used to generate the exception_table of the - * Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May - * be null. - */ - private Handler firstHandler; - - /** - * The last element in the exception handler list (used to generate the exception_table of the - * Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May - * be null. - */ - private Handler lastHandler; - - /** The line_number_table_length field of the LineNumberTable code attribute. */ - private int lineNumberTableLength; - - /** The line_number_table array of the LineNumberTable code attribute, or null. */ - private ByteVector lineNumberTable; - - /** The local_variable_table_length field of the LocalVariableTable code attribute. */ - private int localVariableTableLength; - - /** The local_variable_table array of the LocalVariableTable code attribute, or null. */ - private ByteVector localVariableTable; - - /** The local_variable_type_table_length field of the LocalVariableTypeTable code attribute. */ - private int localVariableTypeTableLength; - - /** - * The local_variable_type_table array of the LocalVariableTypeTable code attribute, or - * null. - */ - private ByteVector localVariableTypeTable; - - /** The number_of_entries field of the StackMapTable code attribute. */ - private int stackMapTableNumberOfEntries; - - /** The 'entries' array of the StackMapTable code attribute. */ - private ByteVector stackMapTableEntries; - - /** - * The first non standard attribute of the Code attribute. The next ones can be accessed with the - * {@link Attribute#nextAttribute} field. May be null. - * - *

WARNING: this list stores the attributes in the reverse order of their visit. - * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link - * #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the - * reverse order specified by the user. - */ - private Attribute firstCodeAttribute; - - // Other method_info attributes: - - /** The number_of_exceptions field of the Exceptions attribute. */ - private final int numberOfExceptions; - - /** The exception_index_table array of the Exceptions attribute, or null. */ - private final int[] exceptionIndexTable; - - /** The signature_index field of the Signature attribute. */ - private final int signatureIndex; - - /** The default_value field of the AnnotationDefault attribute, or null. */ - private ByteVector defaultValue; - - /** The parameters_count field of the MethodParameters attribute. */ - private int parametersCount; - - /** The 'parameters' array of the MethodParameters attribute, or null. */ - private ByteVector parameters; - - /** - * The first non standard attribute of this method. The next ones can be accessed with the {@link - * Attribute#nextAttribute} field. May be null. - * - *

WARNING: this list stores the attributes in the reverse order of their visit. - * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link - * #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the - * reverse order specified by the user. - */ - private Attribute firstAttribute; - - // ----------------------------------------------------------------------------------------------- - // Fields used to compute the maximum stack size and number of locals, and the stack map frames - // ----------------------------------------------------------------------------------------------- - - /** - * Indicates what must be computed. Must be one of {@link #COMPUTE_ALL_FRAMES}, {@link - * #COMPUTE_INSERTED_FRAMES}, {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_NOTHING}. - */ - private final int compute; - - /** - * The first basic block of the method. The next ones (in bytecode offset order) can be accessed - * with the {@link Label#nextBasicBlock} field. - */ - private Label firstBasicBlock; - - /** - * The last basic block of the method (in bytecode offset order). This field is updated each time - * a basic block is encountered, and is used to append it at the end of the basic block list. - */ - private Label lastBasicBlock; - - /** - * The current basic block, i.e. the basic block of the last visited instruction. When {@link - * #compute} is equal to {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_ALL_FRAMES}, this - * field is null for unreachable code. When {@link #compute} is equal to {@link - * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES} or {@link #COMPUTE_INSERTED_FRAMES}, this field stays - * unchanged throughout the whole method (i.e. the whole code is seen as a single basic block; - * indeed, the existing frames are sufficient by hypothesis to compute any intermediate frame - - * and the maximum stack size as well - without using any control flow graph). - */ - private Label currentBasicBlock; - - /** - * The relative stack size after the last visited instruction. This size is relative to the - * beginning of {@link #currentBasicBlock}, i.e. the true stack size after the last visited - * instruction is equal to the {@link Label#inputStackSize} of the current basic block plus {@link - * #relativeStackSize}. When {@link #compute} is equal to {@link - * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of - * the method, so this relative size is also equal to the absolute stack size after the last - * visited instruction. - */ - private int relativeStackSize; - - /** - * The maximum relative stack size after the last visited instruction. This size is relative to - * the beginning of {@link #currentBasicBlock}, i.e. the true maximum stack size after the last - * visited instruction is equal to the {@link Label#inputStackSize} of the current basic block - * plus {@link #maxRelativeStackSize}.When {@link #compute} is equal to {@link - * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of - * the method, so this relative size is also equal to the absolute maximum stack size after the - * last visited instruction. - */ - private int maxRelativeStackSize; - - /** The number of local variables in the last visited stack map frame. */ - private int currentLocals; - - /** The bytecode offset of the last frame that was written in {@link #stackMapTableEntries}. */ - private int previousFrameOffset; - - /** - * The last frame that was written in {@link #stackMapTableEntries}. This field has the same - * format as {@link #currentFrame}. - */ - private int[] previousFrame; - - /** - * The current stack map frame. The first element contains the bytecode offset of the instruction - * to which the frame corresponds, the second element is the number of locals and the third one is - * the number of stack elements. The local variables start at index 3 and are followed by the - * operand stack elements. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = nStack, - * frame[3] = nLocal. Local variables and operand stack entries contain abstract types, as defined - * in {@link Frame}, but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} - * or {@link Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array - * entry. - */ - private int[] currentFrame; - - /** Whether this method contains subroutines. */ - private boolean hasSubroutines; - - // ----------------------------------------------------------------------------------------------- - // Other miscellaneous status fields - // ----------------------------------------------------------------------------------------------- - - /** Whether the bytecode of this method contains ASM specific instructions. */ - private boolean hasAsmInstructions; - - /** - * The start offset of the last visited instruction. Used to set the offset field of type - * annotations of type 'offset_target' (see JVMS - * 4.7.20.1). - */ - private int lastBytecodeOffset; - - /** - * The offset in bytes in {@link SymbolTable#getSource} from which the method_info for this method - * (excluding its first 6 bytes) must be copied, or 0. - */ - private int sourceOffset; - - /** - * The length in bytes in {@link SymbolTable#getSource} which must be copied to get the - * method_info for this method (excluding its first 6 bytes for access_flags, name_index and - * descriptor_index). - */ - private int sourceLength; - - // ----------------------------------------------------------------------------------------------- - // Constructor and accessors - // ----------------------------------------------------------------------------------------------- - - /** - * Constructs a new {@link MethodWriter}. - * - * @param symbolTable where the constants used in this AnnotationWriter must be stored. - * @param access the method's access flags (see {@link Opcodes}). - * @param name the method's name. - * @param descriptor the method's descriptor (see {@link Type}). - * @param signature the method's signature. May be null. - * @param exceptions the internal names of the method's exceptions. May be null. - * @param compute indicates what must be computed (see #compute). - */ - MethodWriter( - final SymbolTable symbolTable, - final int access, - final String name, - final String descriptor, - final String signature, - final String[] exceptions, - final int compute) { - super(Opcodes.ASM6); - this.symbolTable = symbolTable; - this.accessFlags = "".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access; - this.nameIndex = symbolTable.addConstantUtf8(name); - this.descriptorIndex = symbolTable.addConstantUtf8(descriptor); - this.descriptor = descriptor; - this.signatureIndex = signature == null ? 0 : symbolTable.addConstantUtf8(signature); - if (exceptions != null && exceptions.length > 0) { - numberOfExceptions = exceptions.length; - this.exceptionIndexTable = new int[numberOfExceptions]; - for (int i = 0; i < numberOfExceptions; ++i) { - this.exceptionIndexTable[i] = symbolTable.addConstantClass(exceptions[i]).index; - } - } else { - numberOfExceptions = 0; - this.exceptionIndexTable = null; - } - this.compute = compute; - if (compute != COMPUTE_NOTHING) { - // Update maxLocals and currentLocals. - int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2; - if ((access & Opcodes.ACC_STATIC) != 0) { - --argumentsSize; - } - maxLocals = argumentsSize; - currentLocals = argumentsSize; - // Create and visit the label for the first basic block. - firstBasicBlock = new Label(); - visitLabel(firstBasicBlock); - } - } - - boolean hasFrames() { - return stackMapTableNumberOfEntries > 0; - } - - boolean hasAsmInstructions() { - return hasAsmInstructions; - } - - // ----------------------------------------------------------------------------------------------- - // Implementation of the MethodVisitor abstract class - // ----------------------------------------------------------------------------------------------- - - @Override - public void visitParameter(final String name, final int access) { - if (parameters == null) { - parameters = new ByteVector(); - } - ++parametersCount; - parameters.putShort((name == null) ? 0 : symbolTable.addConstantUtf8(name)).putShort(access); - } - - @Override - public void visitAttribute(final Attribute attribute) { - // Store the attributes in the reverse order of their visit by this method. - if (attribute.isCodeAttribute()) { - attribute.nextAttribute = firstCodeAttribute; - firstCodeAttribute = attribute; - } else { - attribute.nextAttribute = firstAttribute; - firstAttribute = attribute; - } - } - - @Override - public void visitCode() { - // Nothing to do. - } - - @Override - public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack) { - if (compute == COMPUTE_ALL_FRAMES) { - return; - } - - if (compute == COMPUTE_INSERTED_FRAMES) { - if (currentBasicBlock.frame == null) { - // This should happen only once, for the implicit first frame (which is explicitly visited - // in ClassReader if the EXPAND_ASM_INSNS option is used - and COMPUTE_INSERTED_FRAMES - // can't be set if EXPAND_ASM_INSNS is not used). - currentBasicBlock.frame = new CurrentFrame(currentBasicBlock); - currentBasicBlock.frame.setInputFrameFromDescriptor( - symbolTable, accessFlags, descriptor, nLocal); - currentBasicBlock.frame.accept(this); - } else { - if (type == Opcodes.F_NEW) { - currentBasicBlock.frame.setInputFrameFromApiFormat( - symbolTable, nLocal, local, nStack, stack); - } else { - // In this case type is equal to F_INSERT by hypothesis, and currentBlock.frame contains - // the stack map frame at the current instruction, computed from the last F_NEW frame - // and the bytecode instructions in between (via calls to CurrentFrame#execute). - } - currentBasicBlock.frame.accept(this); - } - } else if (type == Opcodes.F_NEW) { - if (previousFrame == null) { - int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2; - Frame implicitFirstFrame = new Frame(new Label()); - implicitFirstFrame.setInputFrameFromDescriptor( - symbolTable, accessFlags, descriptor, argumentsSize); - implicitFirstFrame.accept(this); - } - currentLocals = nLocal; - int frameIndex = visitFrameStart(code.length, nLocal, nStack); - for (int i = 0; i < nLocal; ++i) { - currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, local[i]); - } - for (int i = 0; i < nStack; ++i) { - currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, stack[i]); - } - visitFrameEnd(); - } else { - int offsetDelta; - if (stackMapTableEntries == null) { - stackMapTableEntries = new ByteVector(); - offsetDelta = code.length; - } else { - offsetDelta = code.length - previousFrameOffset - 1; - if (offsetDelta < 0) { - if (type == Opcodes.F_SAME) { - return; - } else { - throw new IllegalStateException(); - } - } - } - - switch (type) { - case Opcodes.F_FULL: - currentLocals = nLocal; - stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(nLocal); - for (int i = 0; i < nLocal; ++i) { - putFrameType(local[i]); - } - stackMapTableEntries.putShort(nStack); - for (int i = 0; i < nStack; ++i) { - putFrameType(stack[i]); - } - break; - case Opcodes.F_APPEND: - currentLocals += nLocal; - stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + nLocal).putShort(offsetDelta); - for (int i = 0; i < nLocal; ++i) { - putFrameType(local[i]); - } - break; - case Opcodes.F_CHOP: - currentLocals -= nLocal; - stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED - nLocal).putShort(offsetDelta); - break; - case Opcodes.F_SAME: - if (offsetDelta < 64) { - stackMapTableEntries.putByte(offsetDelta); - } else { - stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta); - } - break; - case Opcodes.F_SAME1: - if (offsetDelta < 64) { - stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta); - } else { - stackMapTableEntries - .putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) - .putShort(offsetDelta); - } - putFrameType(stack[0]); - break; - default: - throw new IllegalArgumentException(); - } - - previousFrameOffset = code.length; - ++stackMapTableNumberOfEntries; - } - - if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { - relativeStackSize = nStack; - if (nStack > maxRelativeStackSize) { - maxRelativeStackSize = relativeStackSize; - } - } - - maxStack = Math.max(maxStack, nStack); - maxLocals = Math.max(maxLocals, currentLocals); - } - - @Override - public void visitInsn(final int opcode) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - code.putByte(opcode); - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, 0, null, null); - } else { - int size = relativeStackSize + STACK_SIZE_DELTA[opcode]; - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) { - endCurrentBasicBlockWithNoSuccessor(); - } - } - } - - @Override - public void visitIntInsn(final int opcode, final int operand) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - if (opcode == Opcodes.SIPUSH) { - code.put12(opcode, operand); - } else { // BIPUSH or NEWARRAY - code.put11(opcode, operand); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, operand, null, null); - } else if (opcode != Opcodes.NEWARRAY) { - // The stack size delta is 1 for BIPUSH or SIPUSH, and 0 for NEWARRAY. - int size = relativeStackSize + 1; - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - if (var < 4 && opcode != Opcodes.RET) { - int optimizedOpcode; - if (opcode < Opcodes.ISTORE) { - optimizedOpcode = Constants.ILOAD_0 + ((opcode - Opcodes.ILOAD) << 2) + var; - } else { - optimizedOpcode = Constants.ISTORE_0 + ((opcode - Opcodes.ISTORE) << 2) + var; - } - code.putByte(optimizedOpcode); - } else if (var >= 256) { - code.putByte(Constants.WIDE).put12(opcode, var); - } else { - code.put11(opcode, var); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, var, null, null); - } else { - if (opcode == Opcodes.RET) { - // No stack size delta. - currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_END; - currentBasicBlock.outputStackSize = (short) relativeStackSize; - endCurrentBasicBlockWithNoSuccessor(); - } else { // xLOAD or xSTORE - int size = relativeStackSize + STACK_SIZE_DELTA[opcode]; - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - if (compute != COMPUTE_NOTHING) { - int currentMaxLocals; - if (opcode == Opcodes.LLOAD - || opcode == Opcodes.DLOAD - || opcode == Opcodes.LSTORE - || opcode == Opcodes.DSTORE) { - currentMaxLocals = var + 2; - } else { - currentMaxLocals = var + 1; - } - if (currentMaxLocals > maxLocals) { - maxLocals = currentMaxLocals; - } - } - if (opcode >= Opcodes.ISTORE && compute == COMPUTE_ALL_FRAMES && firstHandler != null) { - // If there are exception handler blocks, each instruction within a handler range is, in - // theory, a basic block (since execution can jump from this instruction to the exception - // handler). As a consequence, the local variable types at the beginning of the handler - // block should be the merge of the local variable types at all the instructions within the - // handler range. However, instead of creating a basic block for each instruction, we can - // get the same result in a more efficient way. Namely, by starting a new basic block after - // each xSTORE instruction, which is what we do here. - visitLabel(new Label()); - } - } - - @Override - public void visitTypeInsn(final int opcode, final String type) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - Symbol typeSymbol = symbolTable.addConstantClass(type); - code.put12(opcode, typeSymbol.index); - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, lastBytecodeOffset, typeSymbol, symbolTable); - } else if (opcode == Opcodes.NEW) { - // The stack size delta is 1 for NEW, and 0 for ANEWARRAY, CHECKCAST, or INSTANCEOF. - int size = relativeStackSize + 1; - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - - @Override - public void visitFieldInsn( - final int opcode, final String owner, final String name, final String descriptor) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - Symbol fieldrefSymbol = symbolTable.addConstantFieldref(owner, name, descriptor); - code.put12(opcode, fieldrefSymbol.index); - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, 0, fieldrefSymbol, symbolTable); - } else { - int size; - char firstDescChar = descriptor.charAt(0); - switch (opcode) { - case Opcodes.GETSTATIC: - size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 2 : 1); - break; - case Opcodes.PUTSTATIC: - size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -2 : -1); - break; - case Opcodes.GETFIELD: - size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 1 : 0); - break; - case Opcodes.PUTFIELD: - default: - size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -3 : -2); - break; - } - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - - @Override - public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String descriptor, - final boolean isInterface) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - Symbol methodrefSymbol = symbolTable.addConstantMethodref(owner, name, descriptor, isInterface); - if (opcode == Opcodes.INVOKEINTERFACE) { - code.put12(Opcodes.INVOKEINTERFACE, methodrefSymbol.index) - .put11(methodrefSymbol.getArgumentsAndReturnSizes() >> 2, 0); - } else { - code.put12(opcode, methodrefSymbol.index); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(opcode, 0, methodrefSymbol, symbolTable); - } else { - int argumentsAndReturnSize = methodrefSymbol.getArgumentsAndReturnSizes(); - int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2); - int size; - if (opcode == Opcodes.INVOKESTATIC) { - size = relativeStackSize + stackSizeDelta + 1; - } else { - size = relativeStackSize + stackSizeDelta; - } - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - - @Override - public void visitInvokeDynamicInsn( - final String name, - final String descriptor, - final Handle bootstrapMethodHandle, - final Object... bootstrapMethodArguments) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - Symbol invokeDynamicSymbol = - symbolTable.addConstantInvokeDynamic( - name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); - code.put12(Opcodes.INVOKEDYNAMIC, invokeDynamicSymbol.index); - code.putShort(0); - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, invokeDynamicSymbol, symbolTable); - } else { - int argumentsAndReturnSize = invokeDynamicSymbol.getArgumentsAndReturnSizes(); - int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2) + 1; - int size = relativeStackSize + stackSizeDelta; - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - - @Override - public void visitJumpInsn(final int opcode, final Label label) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - // Compute the 'base' opcode, i.e. GOTO or JSR if opcode is GOTO_W or JSR_W, otherwise opcode. - int baseOpcode = - opcode >= Constants.GOTO_W ? opcode - Constants.WIDE_JUMP_OPCODE_DELTA : opcode; - boolean nextInsnIsJumpTarget = false; - if ((label.flags & Label.FLAG_RESOLVED) != 0 - && label.bytecodeOffset - code.length < Short.MIN_VALUE) { - // Case of a backward jump with an offset < -32768. In this case we automatically replace GOTO - // with GOTO_W, JSR with JSR_W and IFxxx with IFNOTxxx GOTO_W L:..., where - // IFNOTxxx is the "opposite" opcode of IFxxx (e.g. IFNE for IFEQ) and where designates - // the instruction just after the GOTO_W. - if (baseOpcode == Opcodes.GOTO) { - code.putByte(Constants.GOTO_W); - } else if (baseOpcode == Opcodes.JSR) { - code.putByte(Constants.JSR_W); - } else { - // Put the "opposite" opcode of baseOpcode. This can be done by flipping the least - // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ (with a - // pre and post offset by 1). The jump offset is 8 bytes (3 for IFNOTxxx, 5 for GOTO_W). - code.putByte(baseOpcode >= Opcodes.IFNULL ? baseOpcode ^ 1 : ((baseOpcode + 1) ^ 1) - 1); - code.putShort(8); - // Here we could put a GOTO_W in theory, but if ASM specific instructions are used in this - // method or another one, and if the class has frames, we will need to insert a frame after - // this GOTO_W during the additional ClassReader -> ClassWriter round trip to remove the ASM - // specific instructions. To not miss this additional frame, we need to use an ASM_GOTO_W - // here, which has the unfortunate effect of forcing this additional round trip (which in - // some case would not have been really necessary, but we can't know this at this point). - code.putByte(Constants.ASM_GOTO_W); - hasAsmInstructions = true; - // The instruction after the GOTO_W becomes the target of the IFNOT instruction. - nextInsnIsJumpTarget = true; - } - label.put(code, code.length - 1, true); - } else if (baseOpcode != opcode) { - // Case of a GOTO_W or JSR_W specified by the user (normally ClassReader when used to remove - // ASM specific instructions). In this case we keep the original instruction. - code.putByte(opcode); - label.put(code, code.length - 1, true); - } else { - // Case of a jump with an offset >= -32768, or of a jump with an unknown offset. In these - // cases we store the offset in 2 bytes (which will be increased via a ClassReader -> - // ClassWriter round trip if it turns out that 2 bytes are not sufficient). - code.putByte(baseOpcode); - label.put(code, code.length - 1, false); - } - - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - Label nextBasicBlock = null; - if (compute == COMPUTE_ALL_FRAMES) { - currentBasicBlock.frame.execute(baseOpcode, 0, null, null); - // Record the fact that 'label' is the target of a jump instruction. - label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; - // Add 'label' as a successor of the current basic block. - addSuccessorToCurrentBasicBlock(Edge.JUMP, label); - if (baseOpcode != Opcodes.GOTO) { - // The next instruction starts a new basic block (except for GOTO: by default the code - // following a goto is unreachable - unless there is an explicit label for it - and we - // should not compute stack frame types for its instructions). - nextBasicBlock = new Label(); - } - } else if (compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(baseOpcode, 0, null, null); - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { - // No need to update maxRelativeStackSize (the stack size delta is always negative). - relativeStackSize += STACK_SIZE_DELTA[baseOpcode]; - } else { - if (baseOpcode == Opcodes.JSR) { - // Record the fact that 'label' designates a subroutine, if not already done. - if ((label.flags & Label.FLAG_SUBROUTINE_START) == 0) { - label.flags |= Label.FLAG_SUBROUTINE_START; - hasSubroutines = true; - } - currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_CALLER; - // Note that, by construction in this method, a block which calls a subroutine has at - // least two successors in the control flow graph: the first one (added below) leads to - // the instruction after the JSR, while the second one (added here) leads to the JSR - // target. Note that the first successor is virtual (it does not correspond to a possible - // execution path): it is only used to compute the successors of the basic blocks ending - // with a ret, in {@link Label#addSubroutineRetSuccessors}. - addSuccessorToCurrentBasicBlock(relativeStackSize + 1, label); - // The instruction after the JSR starts a new basic block. - nextBasicBlock = new Label(); - } else { - // No need to update maxRelativeStackSize (the stack size delta is always negative). - relativeStackSize += STACK_SIZE_DELTA[baseOpcode]; - addSuccessorToCurrentBasicBlock(relativeStackSize, label); - } - } - // If the next instruction starts a new basic block, call visitLabel to add the label of this - // instruction as a successor of the current block, and to start a new basic block. - if (nextBasicBlock != null) { - if (nextInsnIsJumpTarget) { - nextBasicBlock.flags |= Label.FLAG_JUMP_TARGET; - } - visitLabel(nextBasicBlock); - } - if (baseOpcode == Opcodes.GOTO) { - endCurrentBasicBlockWithNoSuccessor(); - } - } - } - - @Override - public void visitLabel(final Label label) { - // Resolve the forward references to this label, if any. - hasAsmInstructions |= label.resolve(code.data, code.length); - // visitLabel starts a new basic block (except for debug only labels), so we need to update the - // previous and current block references and list of successors. - if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) { - return; - } - if (compute == COMPUTE_ALL_FRAMES) { - if (currentBasicBlock != null) { - if (label.bytecodeOffset == currentBasicBlock.bytecodeOffset) { - // We use {@link Label#getCanonicalInstance} to store the state of a basic block in only - // one place, but this does not work for labels which have not been visited yet. - // Therefore, when we detect here two labels having the same bytecode offset, we need to - // - consolidate the state scattered in these two instances into the canonical instance: - currentBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET); - // - make sure the two instances share the same Frame instance (the implementation of - // {@link Label#getCanonicalInstance} relies on this property; here label.frame should be - // null): - label.frame = currentBasicBlock.frame; - // - and make sure to NOT assign 'label' into 'currentBasicBlock' or 'lastBasicBlock', so - // that they still refer to the canonical instance for this bytecode offset. - return; - } - // End the current basic block (with one new successor). - addSuccessorToCurrentBasicBlock(Edge.JUMP, label); - } - // Append 'label' at the end of the basic block list. - if (lastBasicBlock != null) { - if (label.bytecodeOffset == lastBasicBlock.bytecodeOffset) { - // Same comment as above. - lastBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET); - // Here label.frame should be null. - label.frame = lastBasicBlock.frame; - currentBasicBlock = lastBasicBlock; - return; - } - lastBasicBlock.nextBasicBlock = label; - } - lastBasicBlock = label; - // Make it the new current basic block. - currentBasicBlock = label; - // Here label.frame should be null. - label.frame = new Frame(label); - } else if (compute == COMPUTE_INSERTED_FRAMES) { - if (currentBasicBlock == null) { - // This case should happen only once, for the visitLabel call in the constructor. Indeed, if - // compute is equal to COMPUTE_INSERTED_FRAMES, currentBasicBlock stays unchanged. - currentBasicBlock = label; - } else { - // Update the frame owner so that a correct frame offset is computed in Frame.accept(). - currentBasicBlock.frame.owner = label; - } - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { - if (currentBasicBlock != null) { - // End the current basic block (with one new successor). - currentBasicBlock.outputStackMax = (short) maxRelativeStackSize; - addSuccessorToCurrentBasicBlock(relativeStackSize, label); - } - // Start a new current basic block, and reset the current and maximum relative stack sizes. - currentBasicBlock = label; - relativeStackSize = 0; - maxRelativeStackSize = 0; - // Append the new basic block at the end of the basic block list. - if (lastBasicBlock != null) { - lastBasicBlock.nextBasicBlock = label; - } - lastBasicBlock = label; - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES && currentBasicBlock == null) { - // This case should happen only once, for the visitLabel call in the constructor. Indeed, if - // compute is equal to COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES, currentBasicBlock stays - // unchanged. - currentBasicBlock = label; - } - } - - @Override - public void visitLdcInsn(final Object value) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - Symbol constantSymbol = symbolTable.addConstant(value); - int constantIndex = constantSymbol.index; - boolean isLongOrDouble = - constantSymbol.tag == Symbol.CONSTANT_LONG_TAG - || constantSymbol.tag == Symbol.CONSTANT_DOUBLE_TAG; - if (isLongOrDouble) { - code.put12(Constants.LDC2_W, constantIndex); - } else if (constantIndex >= 256) { - code.put12(Constants.LDC_W, constantIndex); - } else { - code.put11(Opcodes.LDC, constantIndex); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute(Opcodes.LDC, 0, constantSymbol, symbolTable); - } else { - int size = relativeStackSize + (isLongOrDouble ? 2 : 1); - if (size > maxRelativeStackSize) { - maxRelativeStackSize = size; - } - relativeStackSize = size; - } - } - } - - @Override - public void visitIincInsn(final int var, final int increment) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - if ((var > 255) || (increment > 127) || (increment < -128)) { - code.putByte(Constants.WIDE).put12(Opcodes.IINC, var).putShort(increment); - } else { - code.putByte(Opcodes.IINC).put11(var, increment); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null - && (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES)) { - currentBasicBlock.frame.execute(Opcodes.IINC, var, null, null); - } - if (compute != COMPUTE_NOTHING) { - int currentMaxLocals = var + 1; - if (currentMaxLocals > maxLocals) { - maxLocals = currentMaxLocals; - } - } - } - - @Override - public void visitTableSwitchInsn( - final int min, final int max, final Label dflt, final Label... labels) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - code.putByte(Opcodes.TABLESWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); - dflt.put(code, lastBytecodeOffset, true); - code.putInt(min).putInt(max); - for (Label label : labels) { - label.put(code, lastBytecodeOffset, true); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - visitSwitchInsn(dflt, labels); - } - - @Override - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - code.putByte(Opcodes.LOOKUPSWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); - dflt.put(code, lastBytecodeOffset, true); - code.putInt(labels.length); - for (int i = 0; i < labels.length; ++i) { - code.putInt(keys[i]); - labels[i].put(code, lastBytecodeOffset, true); - } - // If needed, update the maximum stack size and number of locals, and stack map frames. - visitSwitchInsn(dflt, labels); - } - - private void visitSwitchInsn(final Label dflt, final Label[] labels) { - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES) { - currentBasicBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); - // Add all the labels as successors of the current basic block. - addSuccessorToCurrentBasicBlock(Edge.JUMP, dflt); - dflt.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; - for (Label label : labels) { - addSuccessorToCurrentBasicBlock(Edge.JUMP, label); - label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; - } - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { - // No need to update maxRelativeStackSize (the stack size delta is always negative). - --relativeStackSize; - // Add all the labels as successors of the current basic block. - addSuccessorToCurrentBasicBlock(relativeStackSize, dflt); - for (Label label : labels) { - addSuccessorToCurrentBasicBlock(relativeStackSize, label); - } - } - // End the current basic block. - endCurrentBasicBlockWithNoSuccessor(); - } - } - - @Override - public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { - lastBytecodeOffset = code.length; - // Add the instruction to the bytecode of the method. - Symbol descSymbol = symbolTable.addConstantClass(descriptor); - code.put12(Opcodes.MULTIANEWARRAY, descSymbol.index).putByte(numDimensions); - // If needed, update the maximum stack size and number of locals, and stack map frames. - if (currentBasicBlock != null) { - if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { - currentBasicBlock.frame.execute( - Opcodes.MULTIANEWARRAY, numDimensions, descSymbol, symbolTable); - } else { - // No need to update maxRelativeStackSize (the stack size delta is always negative). - relativeStackSize += 1 - numDimensions; - } - } - } - - @Override - public void visitTryCatchBlock( - final Label start, final Label end, final Label handler, final String type) { - Handler newHandler = - new Handler( - start, end, handler, type != null ? symbolTable.addConstantClass(type).index : 0, type); - if (firstHandler == null) { - firstHandler = newHandler; - } else { - lastHandler.nextHandler = newHandler; - } - lastHandler = newHandler; - } - - @Override - public void visitLocalVariable( - final String name, - final String descriptor, - final String signature, - final Label start, - final Label end, - final int index) { - if (signature != null) { - if (localVariableTypeTable == null) { - localVariableTypeTable = new ByteVector(); - } - ++localVariableTypeTableLength; - localVariableTypeTable - .putShort(start.bytecodeOffset) - .putShort(end.bytecodeOffset - start.bytecodeOffset) - .putShort(symbolTable.addConstantUtf8(name)) - .putShort(symbolTable.addConstantUtf8(signature)) - .putShort(index); - } - if (localVariableTable == null) { - localVariableTable = new ByteVector(); - } - ++localVariableTableLength; - localVariableTable - .putShort(start.bytecodeOffset) - .putShort(end.bytecodeOffset - start.bytecodeOffset) - .putShort(symbolTable.addConstantUtf8(name)) - .putShort(symbolTable.addConstantUtf8(descriptor)) - .putShort(index); - if (compute != COMPUTE_NOTHING) { - char firstDescChar = descriptor.charAt(0); - int currentMaxLocals = index + (firstDescChar == 'J' || firstDescChar == 'D' ? 2 : 1); - if (currentMaxLocals > maxLocals) { - maxLocals = currentMaxLocals; - } - } - } - - @Override - public void visitLineNumber(final int line, final Label start) { - if (lineNumberTable == null) { - lineNumberTable = new ByteVector(); - } - ++lineNumberTableLength; - lineNumberTable.putShort(start.bytecodeOffset); - lineNumberTable.putShort(line); - } - - @Override - public void visitMaxs(final int maxStack, final int maxLocals) { - if (compute == COMPUTE_ALL_FRAMES) { - computeAllFrames(); - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { - computeMaxStackAndLocal(); - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { - this.maxStack = maxRelativeStackSize; - } else { - this.maxStack = maxStack; - this.maxLocals = maxLocals; - } - } - - /** Computes all the stack map frames of the method, from scratch. */ - private void computeAllFrames() { - // Complete the control flow graph with exception handler blocks. - Handler handler = firstHandler; - while (handler != null) { - String catchTypeDescriptor = - handler.catchTypeDescriptor == null ? "java/lang/Throwable" : handler.catchTypeDescriptor; - int catchType = Frame.getAbstractTypeFromInternalName(symbolTable, catchTypeDescriptor); - // Mark handlerBlock as an exception handler. - Label handlerBlock = handler.handlerPc.getCanonicalInstance(); - handlerBlock.flags |= Label.FLAG_JUMP_TARGET; - // Add handlerBlock as a successor of all the basic blocks in the exception handler range. - Label handlerRangeBlock = handler.startPc.getCanonicalInstance(); - Label handlerRangeEnd = handler.endPc.getCanonicalInstance(); - while (handlerRangeBlock != handlerRangeEnd) { - handlerRangeBlock.outgoingEdges = - new Edge(catchType, handlerBlock, handlerRangeBlock.outgoingEdges); - handlerRangeBlock = handlerRangeBlock.nextBasicBlock; - } - handler = handler.nextHandler; - } - - // Create and visit the first (implicit) frame. - Frame firstFrame = firstBasicBlock.frame; - firstFrame.setInputFrameFromDescriptor(symbolTable, accessFlags, descriptor, this.maxLocals); - firstFrame.accept(this); - - // Fix point algorithm: add the first basic block to a list of blocks to process (i.e. blocks - // whose stack map frame has changed) and, while there are blocks to process, remove one from - // the list and update the stack map frames of its successor blocks in the control flow graph - // (which might change them, in which case these blocks must be processed too, and are thus - // added to the list of blocks to process). Also compute the maximum stack size of the method, - // as a by-product. - Label listOfBlocksToProcess = firstBasicBlock; - listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST; - int maxStackSize = 0; - while (listOfBlocksToProcess != Label.EMPTY_LIST) { - // Remove a basic block from the list of blocks to process. - Label basicBlock = listOfBlocksToProcess; - listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; - basicBlock.nextListElement = null; - // By definition, basicBlock is reachable. - basicBlock.flags |= Label.FLAG_REACHABLE; - // Update the (absolute) maximum stack size. - int maxBlockStackSize = basicBlock.frame.getInputStackSize() + basicBlock.outputStackMax; - if (maxBlockStackSize > maxStackSize) { - maxStackSize = maxBlockStackSize; - } - // Update the successor blocks of basicBlock in the control flow graph. - Edge outgoingEdge = basicBlock.outgoingEdges; - while (outgoingEdge != null) { - Label successorBlock = outgoingEdge.successor.getCanonicalInstance(); - boolean successorBlockChanged = - basicBlock.frame.merge(symbolTable, successorBlock.frame, outgoingEdge.info); - if (successorBlockChanged && successorBlock.nextListElement == null) { - // If successorBlock has changed it must be processed. Thus, if it is not already in the - // list of blocks to process, add it to this list. - successorBlock.nextListElement = listOfBlocksToProcess; - listOfBlocksToProcess = successorBlock; - } - outgoingEdge = outgoingEdge.nextEdge; - } - } - - // Loop over all the basic blocks and visit the stack map frames that must be stored in the - // StackMapTable attribute. Also replace unreachable code with NOP* ATHROW, and remove it from - // exception handler ranges. - Label basicBlock = firstBasicBlock; - while (basicBlock != null) { - if ((basicBlock.flags & (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) - == (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) { - basicBlock.frame.accept(this); - } - if ((basicBlock.flags & Label.FLAG_REACHABLE) == 0) { - // Find the start and end bytecode offsets of this unreachable block. - Label nextBasicBlock = basicBlock.nextBasicBlock; - int startOffset = basicBlock.bytecodeOffset; - int endOffset = (nextBasicBlock == null ? code.length : nextBasicBlock.bytecodeOffset) - 1; - if (endOffset >= startOffset) { - // Replace its instructions with NOP ... NOP ATHROW. - for (int i = startOffset; i < endOffset; ++i) { - code.data[i] = Opcodes.NOP; - } - code.data[endOffset] = (byte) Opcodes.ATHROW; - // Emit a frame for this unreachable block, with no local and a Throwable on the stack - // (so that the ATHROW could consume this Throwable if it were reachable). - int frameIndex = visitFrameStart(startOffset, /* nLocal = */ 0, /* nStack = */ 1); - currentFrame[frameIndex] = - Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable"); - visitFrameEnd(); - // Remove this unreachable basic block from the exception handler ranges. - firstHandler = Handler.removeRange(firstHandler, basicBlock, nextBasicBlock); - // The maximum stack size is now at least one, because of the Throwable declared above. - maxStackSize = Math.max(maxStackSize, 1); - } - } - basicBlock = basicBlock.nextBasicBlock; - } - - this.maxStack = maxStackSize; - } - - /** Computes the maximum stack size of the method. */ - private void computeMaxStackAndLocal() { - // Complete the control flow graph with exception handler blocks. - Handler handler = firstHandler; - while (handler != null) { - Label handlerBlock = handler.handlerPc; - Label handlerRangeBlock = handler.startPc; - Label handlerRangeEnd = handler.endPc; - // Add handlerBlock as a successor of all the basic blocks in the exception handler range. - while (handlerRangeBlock != handlerRangeEnd) { - if ((handlerRangeBlock.flags & Label.FLAG_SUBROUTINE_CALLER) == 0) { - handlerRangeBlock.outgoingEdges = - new Edge(Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges); - } else { - // If handlerRangeBlock is a JSR block, add handlerBlock after the first two outgoing - // edges to preserve the hypothesis about JSR block successors order (see - // {@link #visitJumpInsn}). - handlerRangeBlock.outgoingEdges.nextEdge.nextEdge = - new Edge( - Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges.nextEdge.nextEdge); - } - handlerRangeBlock = handlerRangeBlock.nextBasicBlock; - } - handler = handler.nextHandler; - } - - // Complete the control flow graph with the successor blocks of subroutines, if needed. - if (hasSubroutines) { - // First step: find the subroutines. This step determines, for each basic block, to which - // subroutine(s) it belongs. Start with the main "subroutine": - short numSubroutines = 1; - firstBasicBlock.markSubroutine(numSubroutines); - // Then, mark the subroutines called by the main subroutine, then the subroutines called by - // those called by the main subroutine, etc. - for (short currentSubroutine = 1; currentSubroutine <= numSubroutines; ++currentSubroutine) { - Label basicBlock = firstBasicBlock; - while (basicBlock != null) { - if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0 - && basicBlock.subroutineId == currentSubroutine) { - Label jsrTarget = basicBlock.outgoingEdges.nextEdge.successor; - if (jsrTarget.subroutineId == 0) { - // If this subroutine has not been marked yet, find its basic blocks. - jsrTarget.markSubroutine(++numSubroutines); - } - } - basicBlock = basicBlock.nextBasicBlock; - } - } - // Second step: find the successors in the control flow graph of each subroutine basic block - // 'r' ending with a RET instruction. These successors are the virtual successors of the basic - // blocks ending with JSR instructions (see {@link #visitJumpInsn)} that can reach 'r'. - Label basicBlock = firstBasicBlock; - while (basicBlock != null) { - if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) { - // By construction, jsr targets are stored in the second outgoing edge of basic blocks - // that ends with a jsr instruction (see {@link #FLAG_SUBROUTINE_CALLER}). - Label subroutine = basicBlock.outgoingEdges.nextEdge.successor; - subroutine.addSubroutineRetSuccessors(basicBlock); - } - basicBlock = basicBlock.nextBasicBlock; - } - } - - // Data flow algorithm: put the first basic block in a list of blocks to process (i.e. blocks - // whose input stack size has changed) and, while there are blocks to process, remove one - // from the list, update the input stack size of its successor blocks in the control flow - // graph, and add these blocks to the list of blocks to process (if not already done). - Label listOfBlocksToProcess = firstBasicBlock; - listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST; - int maxStackSize = maxStack; - while (listOfBlocksToProcess != Label.EMPTY_LIST) { - // Remove a basic block from the list of blocks to process. Note that we don't reset - // basicBlock.nextListElement to null on purpose, to make sure we don't reprocess already - // processed basic blocks. - Label basicBlock = listOfBlocksToProcess; - listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; - // Compute the (absolute) input stack size and maximum stack size of this block. - int inputStackTop = basicBlock.inputStackSize; - int maxBlockStackSize = inputStackTop + basicBlock.outputStackMax; - // Update the absolute maximum stack size of the method. - if (maxBlockStackSize > maxStackSize) { - maxStackSize = maxBlockStackSize; - } - // Update the input stack size of the successor blocks of basicBlock in the control flow - // graph, and add these blocks to the list of blocks to process, if not already done. - Edge outgoingEdge = basicBlock.outgoingEdges; - if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) { - // Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual - // edges which lead to the instruction just after the jsr, and do not correspond to a - // possible execution path (see {@link #visitJumpInsn} and - // {@link Label#FLAG_SUBROUTINE_CALLER}). - outgoingEdge = outgoingEdge.nextEdge; - } - while (outgoingEdge != null) { - Label successorBlock = outgoingEdge.successor; - if (successorBlock.nextListElement == null) { - successorBlock.inputStackSize = - (short) (outgoingEdge.info == Edge.EXCEPTION ? 1 : inputStackTop + outgoingEdge.info); - successorBlock.nextListElement = listOfBlocksToProcess; - listOfBlocksToProcess = successorBlock; - } - outgoingEdge = outgoingEdge.nextEdge; - } - } - this.maxStack = maxStackSize; - } - - @Override - public void visitEnd() { - // Nothing to do. - } - - // ----------------------------------------------------------------------------------------------- - // Utility methods: control flow analysis algorithm - // ----------------------------------------------------------------------------------------------- - - /** - * Adds a successor to {@link #currentBasicBlock} in the control flow graph. - * - * @param info information about the control flow edge to be added. - * @param successor the successor block to be added to the current basic block. - */ - private void addSuccessorToCurrentBasicBlock(final int info, final Label successor) { - currentBasicBlock.outgoingEdges = new Edge(info, successor, currentBasicBlock.outgoingEdges); - } - - /** - * Ends the current basic block. This method must be used in the case where the current basic - * block does not have any successor. - * - *

WARNING: this method must be called after the currently visited instruction has been put in - * {@link #code} (if frames are computed, this method inserts a new Label to start a new basic - * block after the current instruction). - */ - private void endCurrentBasicBlockWithNoSuccessor() { - if (compute == COMPUTE_ALL_FRAMES) { - Label nextBasicBlock = new Label(); - nextBasicBlock.frame = new Frame(nextBasicBlock); - nextBasicBlock.resolve(code.data, code.length); - lastBasicBlock.nextBasicBlock = nextBasicBlock; - lastBasicBlock = nextBasicBlock; - currentBasicBlock = null; - } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { - currentBasicBlock.outputStackMax = (short) maxRelativeStackSize; - currentBasicBlock = null; - } - } - - // ----------------------------------------------------------------------------------------------- - // Utility methods: stack map frames - // ----------------------------------------------------------------------------------------------- - - /** - * Starts the visit of a new stack map frame, stored in {@link #currentFrame}. - * - * @param offset the bytecode offset of the instruction to which the frame corresponds. - * @param nLocal the number of local variables in the frame. - * @param nStack the number of stack elements in the frame. - * @return the index of the next element to be written in this frame. - */ - int visitFrameStart(final int offset, final int nLocal, final int nStack) { - int frameLength = 3 + nLocal + nStack; - if (currentFrame == null || currentFrame.length < frameLength) { - currentFrame = new int[frameLength]; - } - currentFrame[0] = offset; - currentFrame[1] = nLocal; - currentFrame[2] = nStack; - return 3; - } - - /** - * Sets an abstract type in {@link #currentFrame}. - * - * @param frameIndex the index of the element to be set in {@link #currentFrame}. - * @param abstractType an abstract type. - */ - void visitAbstractType(final int frameIndex, final int abstractType) { - currentFrame[frameIndex] = abstractType; - } - - /** - * Ends the visit of {@link #currentFrame} by writing it in the StackMapTable entries and by - * updating the StackMapTable number_of_entries (except if the current frame is the first one, - * which is implicit in StackMapTable). Then resets {@link #currentFrame} to null. - */ - void visitFrameEnd() { - if (previousFrame != null) { - if (stackMapTableEntries == null) { - stackMapTableEntries = new ByteVector(); - } - putFrame(); - ++stackMapTableNumberOfEntries; - } - previousFrame = currentFrame; - currentFrame = null; - } - - /** Compresses and writes {@link #currentFrame} in a new StackMapTable entry. */ - private void putFrame() { - final int nLocal = currentFrame[1]; - final int nStack = currentFrame[2]; - if (symbolTable.getMajorVersion() < Opcodes.V1_6) { - // Generate a StackMap attribute entry, which are always uncompressed. - stackMapTableEntries.putShort(currentFrame[0]).putShort(nLocal); - putAbstractTypes(3, 3 + nLocal); - stackMapTableEntries.putShort(nStack); - putAbstractTypes(3 + nLocal, 3 + nLocal + nStack); - return; - } - final int offsetDelta = - stackMapTableNumberOfEntries == 0 - ? currentFrame[0] - : currentFrame[0] - previousFrame[0] - 1; - final int previousNlocal = previousFrame[1]; - final int nLocalDelta = nLocal - previousNlocal; - int type = Frame.FULL_FRAME; - if (nStack == 0) { - switch (nLocalDelta) { - case -3: - case -2: - case -1: - type = Frame.CHOP_FRAME; - break; - case 0: - type = offsetDelta < 64 ? Frame.SAME_FRAME : Frame.SAME_FRAME_EXTENDED; - break; - case 1: - case 2: - case 3: - type = Frame.APPEND_FRAME; - break; - default: - // Keep the FULL_FRAME type. - break; - } - } else if (nLocalDelta == 0 && nStack == 1) { - type = - offsetDelta < 63 - ? Frame.SAME_LOCALS_1_STACK_ITEM_FRAME - : Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; - } - if (type != Frame.FULL_FRAME) { - // Verify if locals are the same as in the previous frame. - int frameIndex = 3; - for (int i = 0; i < previousNlocal && i < nLocal; i++) { - if (currentFrame[frameIndex] != previousFrame[frameIndex]) { - type = Frame.FULL_FRAME; - break; - } - frameIndex++; - } - } - switch (type) { - case Frame.SAME_FRAME: - stackMapTableEntries.putByte(offsetDelta); - break; - case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME: - stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta); - putAbstractTypes(3 + nLocal, 4 + nLocal); - break; - case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: - stackMapTableEntries - .putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) - .putShort(offsetDelta); - putAbstractTypes(3 + nLocal, 4 + nLocal); - break; - case Frame.SAME_FRAME_EXTENDED: - stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta); - break; - case Frame.CHOP_FRAME: - stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + nLocalDelta).putShort(offsetDelta); - break; - case Frame.APPEND_FRAME: - stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + nLocalDelta).putShort(offsetDelta); - putAbstractTypes(3 + previousNlocal, 3 + nLocal); - break; - case Frame.FULL_FRAME: - default: - stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(nLocal); - putAbstractTypes(3, 3 + nLocal); - stackMapTableEntries.putShort(nStack); - putAbstractTypes(3 + nLocal, 3 + nLocal + nStack); - } - } - - /** - * Puts some abstract types of {@link #currentFrame} in {@link #stackMapTableEntries} , using the - * JVMS verification_type_info format used in StackMapTable attributes. - * - * @param start index of the first type in {@link #currentFrame} to write. - * @param end index of last type in {@link #currentFrame} to write (exclusive). - */ - private void putAbstractTypes(final int start, final int end) { - for (int i = start; i < end; ++i) { - Frame.putAbstractType(symbolTable, currentFrame[i], stackMapTableEntries); - } - } - - /** - * Puts the given public API frame element type in {@link #stackMapTableEntries} , using the JVMS - * verification_type_info format used in StackMapTable attributes. - * - * @param type a frame element type described using the same format as in {@link - * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link - * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or - * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating - * a NEW instruction (for uninitialized types). - */ - private void putFrameType(final Object type) { - if (type instanceof Integer) { - stackMapTableEntries.putByte(((Integer) type).intValue()); - } else if (type instanceof String) { - stackMapTableEntries - .putByte(Frame.ITEM_OBJECT) - .putShort(symbolTable.addConstantClass((String) type).index); - } else { - stackMapTableEntries - .putByte(Frame.ITEM_UNINITIALIZED) - .putShort(((Label) type).bytecodeOffset); - } - } - - // ----------------------------------------------------------------------------------------------- - // Utility methods - // ----------------------------------------------------------------------------------------------- - - /** - * Returns whether the attributes of this method can be copied from the attributes of the given - * method (assuming there is no method visitor between the given ClassReader and this - * MethodWriter). This method should only be called just after this MethodWriter has been created, - * and before any content is visited. It returns true if the attributes corresponding to the - * constructor arguments (at most a Signature, an Exception, a Deprecated and a Synthetic - * attribute) are the same as the corresponding attributes in the given method. - * - * @param source the source ClassReader from which the attributes of this method might be copied. - * @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which - * the attributes of this method might be copied. - * @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which - * the attributes of this method might be copied. - * @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes - * of this method might be copied contains a Synthetic attribute. - * @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes - * of this method might be copied contains a Deprecated attribute. - * @param signatureIndex the constant pool index contained in the Signature attribute of the - * method_info JVMS structure from which the attributes of this method might be copied, or 0. - * @param exceptionsOffset the offset in 'source.b' of the Exceptions attribute of the method_info - * JVMS structure from which the attributes of this method might be copied, or 0. - * @return whether the attributes of this method can be copied from the attributes of the - * method_info JVMS structure in 'source.b', between 'methodInfoOffset' and 'methodInfoOffset' - * + 'methodInfoLength'. - */ - boolean canCopyMethodAttributes( - final int methodInfoOffset, - final int methodInfoLength, - final boolean hasSyntheticAttribute, - final boolean hasDeprecatedAttribute, - final int signatureIndex, - final int exceptionsOffset) { - boolean needSyntheticAttribute = - symbolTable.getMajorVersion() < Opcodes.V1_5 && (accessFlags & Opcodes.ACC_SYNTHETIC) != 0; - if (hasSyntheticAttribute != needSyntheticAttribute) { - return false; - } - if (exceptionsOffset == 0) { - if (numberOfExceptions != 0) { - return false; - } - } - // Don't copy the attributes yet, instead store their location in the source class reader so - // they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes - // of the method_info JVMS structure. - this.sourceOffset = methodInfoOffset + 6; - this.sourceLength = methodInfoLength - 6; - return true; - } - - /** - * Returns the size of the method_info JVMS structure generated by this MethodWriter. Also add the - * names of the attributes of this method in the constant pool. - * - * @return the size in bytes of the method_info JVMS structure. - */ - int computeMethodInfoSize() { - // If this method_info must be copied from an existing one, the size computation is trivial. - if (sourceOffset != 0) { - // sourceLength excludes the first 6 bytes for access_flags, name_index and descriptor_index. - return 6 + sourceLength; - } - // 2 bytes each for access_flags, name_index, descriptor_index and attributes_count. - int size = 8; - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - if (code.length > 0) { - if (code.length > 65535) { - throw new IndexOutOfBoundsException("Method code too large!"); - } - symbolTable.addConstantUtf8(Constants.CODE); - // The Code attribute has 6 header bytes, plus 2, 2, 4 and 2 bytes respectively for max_stack, - // max_locals, code_length and attributes_count, plus the bytecode and the exception table. - size += 16 + code.length + Handler.getExceptionTableSize(firstHandler); - if (stackMapTableEntries != null) { - boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6; - symbolTable.addConstantUtf8(useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap"); - // 6 header bytes and 2 bytes for number_of_entries. - size += 8 + stackMapTableEntries.length; - } - if (lineNumberTable != null) { - symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE); - // 6 header bytes and 2 bytes for line_number_table_length. - size += 8 + lineNumberTable.length; - } - if (localVariableTable != null) { - symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE); - // 6 header bytes and 2 bytes for local_variable_table_length. - size += 8 + localVariableTable.length; - } - if (localVariableTypeTable != null) { - symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE); - // 6 header bytes and 2 bytes for local_variable_type_table_length. - size += 8 + localVariableTypeTable.length; - } - if (firstCodeAttribute != null) { - size += - firstCodeAttribute.computeAttributesSize( - symbolTable, code.data, code.length, maxStack, maxLocals); - } - } - if (numberOfExceptions > 0) { - symbolTable.addConstantUtf8(Constants.EXCEPTIONS); - size += 8 + 2 * numberOfExceptions; - } - boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - symbolTable.addConstantUtf8(Constants.SYNTHETIC); - size += 6; - } - if (signatureIndex != 0) { - symbolTable.addConstantUtf8(Constants.SIGNATURE); - size += 8; - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - symbolTable.addConstantUtf8(Constants.DEPRECATED); - size += 6; - } - if (defaultValue != null) { - symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT); - size += 6 + defaultValue.length; - } - if (parameters != null) { - symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS); - // 6 header bytes and 1 byte for parameters_count. - size += 7 + parameters.length; - } - if (firstAttribute != null) { - size += firstAttribute.computeAttributesSize(symbolTable); - } - return size; - } - - /** - * Puts the content of the method_info JVMS structure generated by this MethodWriter into the - * given ByteVector. - * - * @param output where the method_info structure must be put. - */ - void putMethodInfo(final ByteVector output) { - boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; - int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0; - output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex); - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - int attributeCount = 0; - if (code.length > 0) { - ++attributeCount; - } - if (numberOfExceptions > 0) { - ++attributeCount; - } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - ++attributeCount; - } - if (signatureIndex != 0) { - ++attributeCount; - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - ++attributeCount; - } - if (defaultValue != null) { - ++attributeCount; - } - if (parameters != null) { - ++attributeCount; - } - if (firstAttribute != null) { - attributeCount += firstAttribute.getAttributeCount(); - } - // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. - output.putShort(attributeCount); - if (code.length > 0) { - // 2, 2, 4 and 2 bytes respectively for max_stack, max_locals, code_length and - // attributes_count, plus the bytecode and the exception table. - int size = 10 + code.length + Handler.getExceptionTableSize(firstHandler); - int codeAttributeCount = 0; - if (stackMapTableEntries != null) { - // 6 header bytes and 2 bytes for number_of_entries. - size += 8 + stackMapTableEntries.length; - ++codeAttributeCount; - } - if (lineNumberTable != null) { - // 6 header bytes and 2 bytes for line_number_table_length. - size += 8 + lineNumberTable.length; - ++codeAttributeCount; - } - if (localVariableTable != null) { - // 6 header bytes and 2 bytes for local_variable_table_length. - size += 8 + localVariableTable.length; - ++codeAttributeCount; - } - if (localVariableTypeTable != null) { - // 6 header bytes and 2 bytes for local_variable_type_table_length. - size += 8 + localVariableTypeTable.length; - ++codeAttributeCount; - } - if (firstCodeAttribute != null) { - size += - firstCodeAttribute.computeAttributesSize( - symbolTable, code.data, code.length, maxStack, maxLocals); - codeAttributeCount += firstCodeAttribute.getAttributeCount(); - } - output - .putShort(symbolTable.addConstantUtf8(Constants.CODE)) - .putInt(size) - .putShort(maxStack) - .putShort(maxLocals) - .putInt(code.length) - .putByteArray(code.data, 0, code.length); - Handler.putExceptionTable(firstHandler, output); - output.putShort(codeAttributeCount); - if (stackMapTableEntries != null) { - boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6; - output - .putShort( - symbolTable.addConstantUtf8( - useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap")) - .putInt(2 + stackMapTableEntries.length) - .putShort(stackMapTableNumberOfEntries) - .putByteArray(stackMapTableEntries.data, 0, stackMapTableEntries.length); - } - if (lineNumberTable != null) { - output - .putShort(symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE)) - .putInt(2 + lineNumberTable.length) - .putShort(lineNumberTableLength) - .putByteArray(lineNumberTable.data, 0, lineNumberTable.length); - } - if (localVariableTable != null) { - output - .putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE)) - .putInt(2 + localVariableTable.length) - .putShort(localVariableTableLength) - .putByteArray(localVariableTable.data, 0, localVariableTable.length); - } - if (localVariableTypeTable != null) { - output - .putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE)) - .putInt(2 + localVariableTypeTable.length) - .putShort(localVariableTypeTableLength) - .putByteArray(localVariableTypeTable.data, 0, localVariableTypeTable.length); - } - if (firstCodeAttribute != null) { - firstCodeAttribute.putAttributes( - symbolTable, code.data, code.length, maxStack, maxLocals, output); - } - } - if (numberOfExceptions > 0) { - output - .putShort(symbolTable.addConstantUtf8(Constants.EXCEPTIONS)) - .putInt(2 + 2 * numberOfExceptions) - .putShort(numberOfExceptions); - for (int exceptionIndex : exceptionIndexTable) { - output.putShort(exceptionIndex); - } - } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); - } - if (signatureIndex != 0) { - output - .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) - .putInt(2) - .putShort(signatureIndex); - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); - } - if (defaultValue != null) { - output - .putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT)) - .putInt(defaultValue.length) - .putByteArray(defaultValue.data, 0, defaultValue.length); - } - if (parameters != null) { - output - .putShort(symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS)) - .putInt(1 + parameters.length) - .putByte(parametersCount) - .putByteArray(parameters.data, 0, parameters.length); - } - if (firstAttribute != null) { - firstAttribute.putAttributes(symbolTable, output); - } - } - - /** - * Collects the attributes of this method into the given set of attribute prototypes. - * - * @param attributePrototypes a set of attribute prototypes. - */ - final void collectAttributePrototypes(final Attribute.Set attributePrototypes) { - attributePrototypes.addAttributes(firstAttribute); - attributePrototypes.addAttributes(firstCodeAttribute); - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Opcodes.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Opcodes.java deleted file mode 100644 index 216f83bc95..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Opcodes.java +++ /dev/null @@ -1,339 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * The JVM opcodes, access flags and array type codes. This interface does not define all the JVM - * opcodes because some opcodes are automatically handled. For example, the xLOAD and xSTORE opcodes - * are automatically replaced by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and - * xSTORE_n opcodes are therefore not defined in this interface. Likewise for LDC, automatically - * replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and JSR_W. - * - * @see JVMS 6 - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public interface Opcodes { - - // ASM API versions. - - int ASM4 = 4 << 16 | 0 << 8; - int ASM5 = 5 << 16 | 0 << 8; - int ASM6 = 6 << 16 | 0 << 8; - - // Java ClassFile versions (the minor version is stored in the 16 most - // significant bits, and the - // major version in the 16 least significant bits). - - int V1_1 = 3 << 16 | 45; - int V1_2 = 0 << 16 | 46; - int V1_3 = 0 << 16 | 47; - int V1_4 = 0 << 16 | 48; - int V1_5 = 0 << 16 | 49; - int V1_6 = 0 << 16 | 50; - int V1_7 = 0 << 16 | 51; - int V1_8 = 0 << 16 | 52; - int V9 = 0 << 16 | 53; - int V10 = 0 << 16 | 54; - int V11 = 0 << 16 | 55; - - /** - * Version flag indicating that the class is using 'preview' features. - * - *

{@code version & V_PREVIEW_EXPERIMENTAL == V_PREVIEW_EXPERIMENTAL} tests if a version is - * flagged with {@code V_PREVIEW_EXPERIMENTAL}. - * - * @deprecated This API is experimental. - */ - @Deprecated int V_PREVIEW_EXPERIMENTAL = 0xFFFF0000; - - // Access flags values, defined in - // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1 - // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5-200-A.1 - // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6-200-A.1 - // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25 - - int ACC_PUBLIC = 0x0001; // class, field, method - int ACC_PRIVATE = 0x0002; // class, field, method - int ACC_PROTECTED = 0x0004; // class, field, method - int ACC_STATIC = 0x0008; // field, method - int ACC_FINAL = 0x0010; // class, field, method, parameter - int ACC_SUPER = 0x0020; // class - int ACC_SYNCHRONIZED = 0x0020; // method - int ACC_OPEN = 0x0020; // module - int ACC_TRANSITIVE = 0x0020; // module requires - int ACC_VOLATILE = 0x0040; // field - int ACC_BRIDGE = 0x0040; // method - int ACC_STATIC_PHASE = 0x0040; // module requires - int ACC_VARARGS = 0x0080; // method - int ACC_TRANSIENT = 0x0080; // field - int ACC_NATIVE = 0x0100; // method - int ACC_INTERFACE = 0x0200; // class - int ACC_ABSTRACT = 0x0400; // class, method - int ACC_STRICT = 0x0800; // method - int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module * - int ACC_ANNOTATION = 0x2000; // class - int ACC_ENUM = 0x4000; // class(?) field inner - int ACC_MANDATED = 0x8000; // parameter, module, module * - int ACC_MODULE = 0x8000; // class - - // ASM specific access flags. - // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard - // access flags, and also to make sure that these flags are automatically filtered out when - // written in class files (because access flags are stored using 16 bits only). - - int ACC_DEPRECATED = 0x20000; // class, field, method - - // Possible values for the type operand of the NEWARRAY instruction. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray. - - int T_BOOLEAN = 4; - int T_CHAR = 5; - int T_FLOAT = 6; - int T_DOUBLE = 7; - int T_BYTE = 8; - int T_SHORT = 9; - int T_INT = 10; - int T_LONG = 11; - - // Possible values for the reference_kind field of CONSTANT_MethodHandle_info structures. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.8. - - int H_GETFIELD = 1; - int H_GETSTATIC = 2; - int H_PUTFIELD = 3; - int H_PUTSTATIC = 4; - int H_INVOKEVIRTUAL = 5; - int H_INVOKESTATIC = 6; - int H_INVOKESPECIAL = 7; - int H_NEWINVOKESPECIAL = 8; - int H_INVOKEINTERFACE = 9; - - // ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}. - - /** An expanded frame. See {@link ClassReader#EXPAND_FRAMES}. */ - int F_NEW = -1; - - /** A compressed frame with complete frame data. */ - int F_FULL = 0; - - /** - * A compressed frame where locals are the same as the locals in the previous frame, except that - * additional 1-3 locals are defined, and with an empty stack. - */ - int F_APPEND = 1; - - /** - * A compressed frame where locals are the same as the locals in the previous frame, except that - * the last 1-3 locals are absent and with an empty stack. - */ - int F_CHOP = 2; - - /** - * A compressed frame with exactly the same locals as the previous frame and with an empty stack. - */ - int F_SAME = 3; - - /** - * A compressed frame with exactly the same locals as the previous frame and with a single value - * on the stack. - */ - int F_SAME1 = 4; - - // Standard stack map frame element types, used in {@link ClassVisitor#visitFrame}. - - Integer TOP = Frame.ITEM_TOP; - Integer INTEGER = Frame.ITEM_INTEGER; - Integer FLOAT = Frame.ITEM_FLOAT; - Integer DOUBLE = Frame.ITEM_DOUBLE; - Integer LONG = Frame.ITEM_LONG; - Integer NULL = Frame.ITEM_NULL; - Integer UNINITIALIZED_THIS = Frame.ITEM_UNINITIALIZED_THIS; - - // The JVM opcode values (with the MethodVisitor method name used to visit them in comment, and - // where '-' means 'same method name as on the previous line'). - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html. - - int NOP = 0; // visitInsn - int ACONST_NULL = 1; // - - int ICONST_M1 = 2; // - - int ICONST_0 = 3; // - - int ICONST_1 = 4; // - - int ICONST_2 = 5; // - - int ICONST_3 = 6; // - - int ICONST_4 = 7; // - - int ICONST_5 = 8; // - - int LCONST_0 = 9; // - - int LCONST_1 = 10; // - - int FCONST_0 = 11; // - - int FCONST_1 = 12; // - - int FCONST_2 = 13; // - - int DCONST_0 = 14; // - - int DCONST_1 = 15; // - - int BIPUSH = 16; // visitIntInsn - int SIPUSH = 17; // - - int LDC = 18; // visitLdcInsn - int ILOAD = 21; // visitVarInsn - int LLOAD = 22; // - - int FLOAD = 23; // - - int DLOAD = 24; // - - int ALOAD = 25; // - - int IALOAD = 46; // visitInsn - int LALOAD = 47; // - - int FALOAD = 48; // - - int DALOAD = 49; // - - int AALOAD = 50; // - - int BALOAD = 51; // - - int CALOAD = 52; // - - int SALOAD = 53; // - - int ISTORE = 54; // visitVarInsn - int LSTORE = 55; // - - int FSTORE = 56; // - - int DSTORE = 57; // - - int ASTORE = 58; // - - int IASTORE = 79; // visitInsn - int LASTORE = 80; // - - int FASTORE = 81; // - - int DASTORE = 82; // - - int AASTORE = 83; // - - int BASTORE = 84; // - - int CASTORE = 85; // - - int SASTORE = 86; // - - int POP = 87; // - - int POP2 = 88; // - - int DUP = 89; // - - int DUP_X1 = 90; // - - int DUP_X2 = 91; // - - int DUP2 = 92; // - - int DUP2_X1 = 93; // - - int DUP2_X2 = 94; // - - int SWAP = 95; // - - int IADD = 96; // - - int LADD = 97; // - - int FADD = 98; // - - int DADD = 99; // - - int ISUB = 100; // - - int LSUB = 101; // - - int FSUB = 102; // - - int DSUB = 103; // - - int IMUL = 104; // - - int LMUL = 105; // - - int FMUL = 106; // - - int DMUL = 107; // - - int IDIV = 108; // - - int LDIV = 109; // - - int FDIV = 110; // - - int DDIV = 111; // - - int IREM = 112; // - - int LREM = 113; // - - int FREM = 114; // - - int DREM = 115; // - - int INEG = 116; // - - int LNEG = 117; // - - int FNEG = 118; // - - int DNEG = 119; // - - int ISHL = 120; // - - int LSHL = 121; // - - int ISHR = 122; // - - int LSHR = 123; // - - int IUSHR = 124; // - - int LUSHR = 125; // - - int IAND = 126; // - - int LAND = 127; // - - int IOR = 128; // - - int LOR = 129; // - - int IXOR = 130; // - - int LXOR = 131; // - - int IINC = 132; // visitIincInsn - int I2L = 133; // visitInsn - int I2F = 134; // - - int I2D = 135; // - - int L2I = 136; // - - int L2F = 137; // - - int L2D = 138; // - - int F2I = 139; // - - int F2L = 140; // - - int F2D = 141; // - - int D2I = 142; // - - int D2L = 143; // - - int D2F = 144; // - - int I2B = 145; // - - int I2C = 146; // - - int I2S = 147; // - - int LCMP = 148; // - - int FCMPL = 149; // - - int FCMPG = 150; // - - int DCMPL = 151; // - - int DCMPG = 152; // - - int IFEQ = 153; // visitJumpInsn - int IFNE = 154; // - - int IFLT = 155; // - - int IFGE = 156; // - - int IFGT = 157; // - - int IFLE = 158; // - - int IF_ICMPEQ = 159; // - - int IF_ICMPNE = 160; // - - int IF_ICMPLT = 161; // - - int IF_ICMPGE = 162; // - - int IF_ICMPGT = 163; // - - int IF_ICMPLE = 164; // - - int IF_ACMPEQ = 165; // - - int IF_ACMPNE = 166; // - - int GOTO = 167; // - - int JSR = 168; // - - int RET = 169; // visitVarInsn - int TABLESWITCH = 170; // visiTableSwitchInsn - int LOOKUPSWITCH = 171; // visitLookupSwitch - int IRETURN = 172; // visitInsn - int LRETURN = 173; // - - int FRETURN = 174; // - - int DRETURN = 175; // - - int ARETURN = 176; // - - int RETURN = 177; // - - int GETSTATIC = 178; // visitFieldInsn - int PUTSTATIC = 179; // - - int GETFIELD = 180; // - - int PUTFIELD = 181; // - - int INVOKEVIRTUAL = 182; // visitMethodInsn - int INVOKESPECIAL = 183; // - - int INVOKESTATIC = 184; // - - int INVOKEINTERFACE = 185; // - - int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn - int NEW = 187; // visitTypeInsn - int NEWARRAY = 188; // visitIntInsn - int ANEWARRAY = 189; // visitTypeInsn - int ARRAYLENGTH = 190; // visitInsn - int ATHROW = 191; // - - int CHECKCAST = 192; // visitTypeInsn - int INSTANCEOF = 193; // - - int MONITORENTER = 194; // visitInsn - int MONITOREXIT = 195; // - - int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn - int IFNULL = 198; // visitJumpInsn - int IFNONNULL = 199; // - -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Symbol.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Symbol.java deleted file mode 100644 index 8435cc5252..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Symbol.java +++ /dev/null @@ -1,240 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * An entry of the constant pool, of the BootstrapMethods attribute, or of the (ASM specific) type - * table of a class. - * - * @see JVMS - * 4.4 - * @see JVMS - * 4.7.23 - * @author Eric Bruneton - */ -abstract class Symbol { - - // Tag values for the constant pool entries (using the same order as in the JVMS). - - /** The tag value of CONSTANT_Class_info JVMS structures. */ - static final int CONSTANT_CLASS_TAG = 7; - - /** The tag value of CONSTANT_Fieldref_info JVMS structures. */ - static final int CONSTANT_FIELDREF_TAG = 9; - - /** The tag value of CONSTANT_Methodref_info JVMS structures. */ - static final int CONSTANT_METHODREF_TAG = 10; - - /** The tag value of CONSTANT_InterfaceMethodref_info JVMS structures. */ - static final int CONSTANT_INTERFACE_METHODREF_TAG = 11; - - /** The tag value of CONSTANT_String_info JVMS structures. */ - static final int CONSTANT_STRING_TAG = 8; - - /** The tag value of CONSTANT_Integer_info JVMS structures. */ - static final int CONSTANT_INTEGER_TAG = 3; - - /** The tag value of CONSTANT_Float_info JVMS structures. */ - static final int CONSTANT_FLOAT_TAG = 4; - - /** The tag value of CONSTANT_Long_info JVMS structures. */ - static final int CONSTANT_LONG_TAG = 5; - - /** The tag value of CONSTANT_Double_info JVMS structures. */ - static final int CONSTANT_DOUBLE_TAG = 6; - - /** The tag value of CONSTANT_NameAndType_info JVMS structures. */ - static final int CONSTANT_NAME_AND_TYPE_TAG = 12; - - /** The tag value of CONSTANT_Utf8_info JVMS structures. */ - static final int CONSTANT_UTF8_TAG = 1; - - /** The tag value of CONSTANT_MethodHandle_info JVMS structures. */ - static final int CONSTANT_METHOD_HANDLE_TAG = 15; - - /** The tag value of CONSTANT_MethodType_info JVMS structures. */ - static final int CONSTANT_METHOD_TYPE_TAG = 16; - - /** The tag value of CONSTANT_Dynamic_info JVMS structures. */ - static final int CONSTANT_DYNAMIC_TAG = 17; - - /** The tag value of CONSTANT_InvokeDynamic_info JVMS structures. */ - static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18; - - /** The tag value of CONSTANT_Module_info JVMS structures. */ - static final int CONSTANT_MODULE_TAG = 19; - - /** The tag value of CONSTANT_Package_info JVMS structures. */ - static final int CONSTANT_PACKAGE_TAG = 20; - - // Tag values for the BootstrapMethods attribute entries (ASM specific tag). - - /** The tag value of the BootstrapMethods attribute entries. */ - static final int BOOTSTRAP_METHOD_TAG = 64; - - // Tag values for the type table entries (ASM specific tags). - - /** The tag value of a normal type entry in the (ASM specific) type table of a class. */ - static final int TYPE_TAG = 128; - - /** - * The tag value of an {@link Frame#ITEM_UNINITIALIZED} type entry in the type table of a class. - */ - static final int UNINITIALIZED_TYPE_TAG = 129; - - /** The tag value of a merged type entry in the (ASM specific) type table of a class. */ - static final int MERGED_TYPE_TAG = 130; - - // Instance fields. - - /** - * The index of this symbol in the constant pool, in the BootstrapMethods attribute, or in the - * (ASM specific) type table of a class (depending on the {@link #tag} value). - */ - final int index; - - /** - * A tag indicating the type of this symbol. Must be one of the static tag values defined in this - * class. - */ - final int tag; - - /** - * The internal name of the owner class of this symbol. Only used for {@link - * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link - * #CONSTANT_INTERFACE_METHODREF_TAG}, and {@link #CONSTANT_METHOD_HANDLE_TAG} symbols. - */ - final String owner; - - /** - * The name of the class field or method corresponding to this symbol. Only used for {@link - * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link - * #CONSTANT_INTERFACE_METHODREF_TAG}, {@link #CONSTANT_NAME_AND_TYPE_TAG}, {@link - * #CONSTANT_METHOD_HANDLE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link - * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols. - */ - final String name; - - /** - * The string value of this symbol. This is: - * - *

    - *
  • a field or method descriptor for {@link #CONSTANT_FIELDREF_TAG}, {@link - * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG}, {@link - * #CONSTANT_NAME_AND_TYPE_TAG}, {@link #CONSTANT_METHOD_HANDLE_TAG}, {@link - * #CONSTANT_METHOD_TYPE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link - * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, - *
  • an arbitrary string for {@link #CONSTANT_UTF8_TAG} and {@link #CONSTANT_STRING_TAG} - * symbols, - *
  • an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG} and {@link - * #UNINITIALIZED_TYPE_TAG} symbols, - *
  • null for the other types of symbol. - *
- */ - final String value; - - /** - * The numeric value of this symbol. This is: - * - *
    - *
  • the symbol's value for {@link #CONSTANT_INTEGER_TAG},{@link #CONSTANT_FLOAT_TAG}, {@link - * #CONSTANT_LONG_TAG}, {@link #CONSTANT_DOUBLE_TAG}, - *
  • the CONSTANT_MethodHandle_info reference_kind field value for {@link - * #CONSTANT_METHOD_HANDLE_TAG} symbols, - *
  • the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link - * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, - *
  • the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for - * {@link #CONSTANT_DYNAMIC_TAG} or {@link #BOOTSTRAP_METHOD_TAG} symbols, - *
  • the bytecode offset of the NEW instruction that created an {@link - * Frame#ITEM_UNINITIALIZED} type for {@link #UNINITIALIZED_TYPE_TAG} symbols, - *
  • the indices (in the class' type table) of two {@link #TYPE_TAG} source types for {@link - * #MERGED_TYPE_TAG} symbols, - *
  • 0 for the other types of symbol. - *
- */ - final long data; - - /** - * Additional information about this symbol, generally computed lazily. Warning: the value of - * this field is ignored when comparing Symbol instances (to avoid duplicate entries in a - * SymbolTable). Therefore, this field should only contain data that can be computed from the - * other fields of this class. It contains: - * - *
    - *
  • the {@link Type#getArgumentsAndReturnSizes} of the symbol's method descriptor for {@link - * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link - * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, - *
  • the index in the InnerClasses_attribute 'classes' array (plus one) corresponding to this - * class, for {@link #CONSTANT_CLASS_TAG} symbols, - *
  • the index (in the class' type table) of the merged type of the two source types for - * {@link #MERGED_TYPE_TAG} symbols, - *
  • 0 for the other types of symbol, or if this field has not been computed yet. - *
- */ - int info; - - /** - * Constructs a new Symbol. This constructor can't be used directly because the Symbol class is - * abstract. Instead, use the factory methods of the {@link SymbolTable} class. - * - * @param index the symbol index in the constant pool, in the BootstrapMethods attribute, or in - * the (ASM specific) type table of a class (depending on 'tag'). - * @param tag the symbol type. Must be one of the static tag values defined in this class. - * @param owner The internal name of the symbol's owner class. Maybe null. - * @param name The name of the symbol's corresponding class field or method. Maybe null. - * @param value The string value of this symbol. Maybe null. - * @param data The numeric value of this symbol. - */ - Symbol( - final int index, - final int tag, - final String owner, - final String name, - final String value, - final long data) { - this.index = index; - this.tag = tag; - this.owner = owner; - this.name = name; - this.value = value; - this.data = data; - } - - /** - * @return the result {@link Type#getArgumentsAndReturnSizes} on {@link #value} (memoized in - * {@link #info} for efficiency). This should only be used for {@link - * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link - * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols. - */ - int getArgumentsAndReturnSizes() { - if (info == 0) { - info = Type.getArgumentsAndReturnSizes(value); - } - return info; - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/SymbolTable.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/SymbolTable.java deleted file mode 100644 index 602c76ada4..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/SymbolTable.java +++ /dev/null @@ -1,1121 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -/** - * The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type - * table entries of a class. - * - * @see JVMS - * 4.4 - * @see JVMS - * 4.7.23 - * @author Eric Bruneton - */ -final class SymbolTable { - - /** - * An entry of a SymbolTable. This concrete and private subclass of {@link Symbol} adds two fields - * which are only used inside SymbolTable, to implement hash sets of symbols (in order to avoid - * duplicate symbols). See {@link #entries}. - * - * @author Eric Bruneton - */ - private static class Entry extends Symbol { - - /** The hash code of this entry. */ - final int hashCode; - - /** - * Another entry (and so on recursively) having the same hash code (modulo the size of {@link - * #entries}) as this one. - */ - Entry next; - - Entry( - final int index, - final int tag, - final String owner, - final String name, - final String value, - final long data, - final int hashCode) { - super(index, tag, owner, name, value, data); - this.hashCode = hashCode; - } - - Entry(final int index, final int tag, final String value, final int hashCode) { - super(index, tag, /* owner = */ null, /* name = */ null, value, /* data = */ 0); - this.hashCode = hashCode; - } - - Entry(final int index, final int tag, final String value, final long data, final int hashCode) { - super(index, tag, /* owner = */ null, /* name = */ null, value, data); - this.hashCode = hashCode; - } - - Entry( - final int index, final int tag, final String name, final String value, final int hashCode) { - super(index, tag, /* owner = */ null, name, value, /* data = */ 0); - this.hashCode = hashCode; - } - - Entry(final int index, final int tag, final long data, final int hashCode) { - super(index, tag, /* owner = */ null, /* name = */ null, /* value = */ null, data); - this.hashCode = hashCode; - } - } - - /** - * The ClassWriter to which this SymbolTable belongs. This is only used to get access to {@link - * ClassWriter#getCommonSuperClass} and to serialize custom attributes with {@link - * Attribute#write}. - */ - final ClassWriter classWriter; - - /** The major version number of the class to which this symbol table belongs. */ - private int majorVersion; - - /** The internal name of the class to which this symbol table belongs. */ - private String className; - - /** - * The total number of {@link Entry} instances in {@link #entries}. This includes entries that are - * accessible (recursively) via {@link Entry#next}. - */ - private int entryCount; - - /** - * A hash set of all the entries in this SymbolTable (this includes the constant pool entries, the - * bootstrap method entries and the type table entries). Each {@link Entry} instance is stored at - * the array index given by its hash code modulo the array size. If several entries must be stored - * at the same array index, they are linked together via their {@link Entry#next} field. The - * factory methods of this class make sure that this table does not contain duplicated entries. - */ - private Entry[] entries; - - /** - * The number of constant pool items in {@link #constantPool}, plus 1. The first constant pool - * item has index 1, and long and double items count for two items. - */ - private int constantPoolCount; - - /** - * The content of the ClassFile's constant_pool JVMS structure corresponding to this SymbolTable. - * The ClassFile's constant_pool_count field is not included. - */ - private ByteVector constantPool; - - /** - * The number of bootstrap methods in {@link #bootstrapMethods}. Corresponds to the - * BootstrapMethods_attribute's num_bootstrap_methods field value. - */ - private int bootstrapMethodCount; - - /** - * The content of the BootstrapMethods attribute 'bootstrap_methods' array corresponding to this - * SymbolTable. Note that the first 6 bytes of the BootstrapMethods_attribute, and its - * num_bootstrap_methods field, are not included. - */ - private ByteVector bootstrapMethods; - - /** - * The actual number of elements in {@link #typeTable}. These elements are stored from index 0 to - * typeCount (excluded). The other array entries are empty. - */ - private int typeCount; - - /** - * An ASM specific type table used to temporarily store internal names that will not necessarily - * be stored in the constant pool. This type table is used by the control flow and data flow - * analysis algorithm used to compute stack map frames from scratch. This array stores {@link - * Symbol#TYPE_TAG} and {@link Symbol#UNINITIALIZED_TYPE_TAG}) Symbol. The type symbol at index - * i has its {@link Symbol#index} equal to i (and vice versa). - */ - private Entry[] typeTable; - - /** - * Constructs a new, empty SymbolTable for the given ClassWriter. - * - * @param classWriter a ClassWriter. - */ - SymbolTable(final ClassWriter classWriter) { - this.classWriter = classWriter; - this.entries = new Entry[256]; - this.constantPoolCount = 1; - this.constantPool = new ByteVector(); - } - - /** @return the major version of the class to which this symbol table belongs. */ - int getMajorVersion() { - return majorVersion; - } - - /** @return the internal name of the class to which this symbol table belongs. */ - String getClassName() { - return className; - } - - /** - * Sets the major version and the name of the class to which this symbol table belongs. Also adds - * the class name to the constant pool. - * - * @param majorVersion a major ClassFile version number. - * @param className an internal class name. - * @return the constant pool index of a new or already existing Symbol with the given class name. - */ - int setMajorVersionAndClassName(final int majorVersion, final String className) { - this.majorVersion = majorVersion; - this.className = className; - return addConstantClass(className).index; - } - - /** @return the number of items in this symbol table's constant_pool array (plus 1). */ - int getConstantPoolCount() { - return constantPoolCount; - } - - /** @return the length in bytes of this symbol table's constant_pool array. */ - int getConstantPoolLength() { - return constantPool.length; - } - - /** - * Puts this symbol table's constant_pool array in the given ByteVector, preceded by the - * constant_pool_count value. - * - * @param output where the JVMS ClassFile's constant_pool array must be put. - */ - void putConstantPool(final ByteVector output) { - output.putShort(constantPoolCount).putByteArray(constantPool.data, 0, constantPool.length); - } - - /** - * Returns the size in bytes of this symbol table's BootstrapMethods attribute. Also adds the - * attribute name in the constant pool. - * - * @return the size in bytes of this symbol table's BootstrapMethods attribute. - */ - int computeBootstrapMethodsSize() { - if (bootstrapMethods != null) { - addConstantUtf8(Constants.BOOTSTRAP_METHODS); - return 8 + bootstrapMethods.length; - } else { - return 0; - } - } - - /** - * Puts this symbol table's BootstrapMethods attribute in the given ByteVector. This includes the - * 6 attribute header bytes and the num_bootstrap_methods value. - * - * @param output where the JVMS BootstrapMethods attribute must be put. - */ - void putBootstrapMethods(final ByteVector output) { - if (bootstrapMethods != null) { - output - .putShort(addConstantUtf8(Constants.BOOTSTRAP_METHODS)) - .putInt(bootstrapMethods.length + 2) - .putShort(bootstrapMethodCount) - .putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); - } - } - - // ----------------------------------------------------------------------------------------------- - // Generic symbol table entries management. - // ----------------------------------------------------------------------------------------------- - - /** - * @param hashCode a {@link Entry#hashCode} value. - * @return the list of entries which can potentially have the given hash code. The list is stored - * via the {@link Entry#next} field. - */ - private Entry get(final int hashCode) { - return entries[hashCode % entries.length]; - } - - /** - * Puts the given entry in the {@link #entries} hash set. This method does not check - * whether {@link #entries} already contains a similar entry or not. {@link #entries} is resized - * if necessary to avoid hash collisions (multiple entries needing to be stored at the same {@link - * #entries} array index) as much as possible, with reasonable memory usage. - * - * @param entry an Entry (which must not already be contained in {@link #entries}). - * @return the given entry - */ - private Entry put(final Entry entry) { - if (entryCount > (entries.length * 3) / 4) { - int currentCapacity = entries.length; - int newCapacity = currentCapacity * 2 + 1; - Entry[] newEntries = new Entry[newCapacity]; - for (int i = currentCapacity - 1; i >= 0; --i) { - Entry currentEntry = entries[i]; - while (currentEntry != null) { - int newCurrentEntryIndex = currentEntry.hashCode % newCapacity; - Entry nextEntry = currentEntry.next; - currentEntry.next = newEntries[newCurrentEntryIndex]; - newEntries[newCurrentEntryIndex] = currentEntry; - currentEntry = nextEntry; - } - } - entries = newEntries; - } - entryCount++; - int index = entry.hashCode % entries.length; - entry.next = entries[index]; - return entries[index] = entry; - } - - /** - * Adds the given entry in the {@link #entries} hash set. This method does not check - * whether {@link #entries} already contains a similar entry or not, and does not resize - * {@link #entries} if necessary. - * - * @param entry an Entry (which must not already be contained in {@link #entries}). - */ - private void add(final Entry entry) { - entryCount++; - int index = entry.hashCode % entries.length; - entry.next = entries[index]; - entries[index] = entry; - } - - // ----------------------------------------------------------------------------------------------- - // Constant pool entries management. - // ----------------------------------------------------------------------------------------------- - - /** - * Adds a number or string constant to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value the value of the constant to be added to the constant pool. This parameter must be - * an {@link Integer}, {@link Byte}, {@link Character}, {@link Short}, {@link Boolean}, {@link - * Float}, {@link Long}, {@link Double}, {@link String}, {@link Type} or {@link Handle}. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstant(final Object value) { - if (value instanceof Integer) { - return addConstantInteger(((Integer) value).intValue()); - } else if (value instanceof Byte) { - return addConstantInteger(((Byte) value).intValue()); - } else if (value instanceof Character) { - return addConstantInteger(((Character) value).charValue()); - } else if (value instanceof Short) { - return addConstantInteger(((Short) value).intValue()); - } else if (value instanceof Boolean) { - return addConstantInteger(((Boolean) value).booleanValue() ? 1 : 0); - } else if (value instanceof Float) { - return addConstantFloat(((Float) value).floatValue()); - } else if (value instanceof Long) { - return addConstantLong(((Long) value).longValue()); - } else if (value instanceof Double) { - return addConstantDouble(((Double) value).doubleValue()); - } else if (value instanceof String) { - return addConstantString((String) value); - } else if (value instanceof Type) { - Type type = (Type) value; - int typeSort = type.getSort(); - if (typeSort == Type.OBJECT) { - return addConstantClass(type.getInternalName()); - } else if (typeSort == Type.METHOD) { - return addConstantMethodType(type.getDescriptor()); - } else { // type is a primitive or array type. - return addConstantClass(type.getDescriptor()); - } - } else if (value instanceof Handle) { - Handle handle = (Handle) value; - return addConstantMethodHandle( - handle.getTag(), - handle.getOwner(), - handle.getName(), - handle.getDesc(), - handle.isInterface()); - } else { - throw new IllegalArgumentException("value " + value); - } - } - - /** - * Adds a CONSTANT_Class_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value the internal name of a class. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantClass(final String value) { - return addConstantUtf8Reference(Symbol.CONSTANT_CLASS_TAG, value); - } - - /** - * Adds a CONSTANT_Fieldref_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param owner the internal name of a class. - * @param name a field name. - * @param descriptor a field descriptor. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantFieldref(final String owner, final String name, final String descriptor) { - return addConstantMemberReference(Symbol.CONSTANT_FIELDREF_TAG, owner, name, descriptor); - } - - /** - * Adds a CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to the constant pool of this - * symbol table. Does nothing if the constant pool already contains a similar item. - * - * @param owner the internal name of a class. - * @param name a method name. - * @param descriptor a method descriptor. - * @param isInterface whether owner is an interface or not. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantMethodref( - final String owner, final String name, final String descriptor, final boolean isInterface) { - int tag = isInterface ? Symbol.CONSTANT_INTERFACE_METHODREF_TAG : Symbol.CONSTANT_METHODREF_TAG; - return addConstantMemberReference(tag, owner, name, descriptor); - } - - /** - * Adds a CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to - * the constant pool of this symbol table. Does nothing if the constant pool already contains a - * similar item. - * - * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG} - * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}. - * @param owner the internal name of a class. - * @param name a field or method name. - * @param descriptor a field or method descriptor. - * @return a new or already existing Symbol with the given value. - */ - private Entry addConstantMemberReference( - final int tag, final String owner, final String name, final String descriptor) { - int hashCode = hash(tag, owner, name, descriptor); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag - && entry.hashCode == hashCode - && entry.owner.equals(owner) - && entry.name.equals(name) - && entry.value.equals(descriptor)) { - return entry; - } - entry = entry.next; - } - constantPool.put122( - tag, addConstantClass(owner).index, addConstantNameAndType(name, descriptor)); - return put(new Entry(constantPoolCount++, tag, owner, name, descriptor, 0, hashCode)); - } - - /** - * Adds a new CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info - * to the constant pool of this symbol table. - * - * @param index the constant pool index of the new Symbol. - * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG} - * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}. - * @param owner the internal name of a class. - * @param name a field or method name. - * @param descriptor a field or method descriptor. - */ - private void addConstantMemberReference( - final int index, - final int tag, - final String owner, - final String name, - final String descriptor) { - add(new Entry(index, tag, owner, name, descriptor, 0, hash(tag, owner, name, descriptor))); - } - - /** - * Adds a CONSTANT_String_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value a string. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantString(final String value) { - return addConstantUtf8Reference(Symbol.CONSTANT_STRING_TAG, value); - } - - /** - * Adds a CONSTANT_Integer_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value an int. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantInteger(final int value) { - return addConstantInteger(Symbol.CONSTANT_INTEGER_TAG, value); - } - - /** - * Adds a CONSTANT_Float_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value a float. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantFloat(final float value) { - return addConstantInteger(Symbol.CONSTANT_FLOAT_TAG, Float.floatToRawIntBits(value)); - } - - /** - * Adds a CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol table. - * Does nothing if the constant pool already contains a similar item. - * - * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. - * @param value an int or float. - * @return a constant pool constant with the given tag and primitive values. - */ - private Symbol addConstantInteger(final int tag, final int value) { - int hashCode = hash(tag, value); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { - return entry; - } - entry = entry.next; - } - constantPool.putByte(tag).putInt(value); - return put(new Entry(constantPoolCount++, tag, value, hashCode)); - } - - /** - * Adds a new CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol - * table. - * - * @param index the constant pool index of the new Symbol. - * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. - * @param value an int or float. - */ - private void addConstantInteger(final int index, final int tag, final int value) { - add(new Entry(index, tag, value, hash(tag, value))); - } - - /** - * Adds a CONSTANT_Long_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value a long. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantLong(final long value) { - return addConstantLong(Symbol.CONSTANT_LONG_TAG, value); - } - - /** - * Adds a CONSTANT_Double_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value a double. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantDouble(final double value) { - return addConstantLong(Symbol.CONSTANT_DOUBLE_TAG, Double.doubleToRawLongBits(value)); - } - - /** - * Adds a CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol table. - * Does nothing if the constant pool already contains a similar item. - * - * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}. - * @param value a long or double. - * @return a constant pool constant with the given tag and primitive values. - */ - private Symbol addConstantLong(final int tag, final long value) { - int hashCode = hash(tag, value); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { - return entry; - } - entry = entry.next; - } - int index = constantPoolCount; - constantPool.putByte(tag).putLong(value); - constantPoolCount += 2; - return put(new Entry(index, tag, value, hashCode)); - } - - /** - * Adds a new CONSTANT_Double_info to the constant pool of this symbol table. - * - * @param index the constant pool index of the new Symbol. - * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}. - * @param value a long or double. - */ - private void addConstantLong(final int index, final int tag, final long value) { - add(new Entry(index, tag, value, hash(tag, value))); - } - - /** - * Adds a CONSTANT_NameAndType_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param name a field or method name. - * @param descriptor a field or method descriptor. - * @return a new or already existing Symbol with the given value. - */ - int addConstantNameAndType(final String name, final String descriptor) { - final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG; - int hashCode = hash(tag, name, descriptor); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag - && entry.hashCode == hashCode - && entry.name.equals(name) - && entry.value.equals(descriptor)) { - return entry.index; - } - entry = entry.next; - } - constantPool.put122(tag, addConstantUtf8(name), addConstantUtf8(descriptor)); - return put(new Entry(constantPoolCount++, tag, name, descriptor, hashCode)).index; - } - - /** - * Adds a new CONSTANT_NameAndType_info to the constant pool of this symbol table. - * - * @param index the constant pool index of the new Symbol. - * @param name a field or method name. - * @param descriptor a field or method descriptor. - */ - private void addConstantNameAndType(final int index, final String name, final String descriptor) { - final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG; - add(new Entry(index, tag, name, descriptor, hash(tag, name, descriptor))); - } - - /** - * Adds a CONSTANT_Utf8_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param value a string. - * @return a new or already existing Symbol with the given value. - */ - int addConstantUtf8(final String value) { - int hashCode = hash(Symbol.CONSTANT_UTF8_TAG, value); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == Symbol.CONSTANT_UTF8_TAG - && entry.hashCode == hashCode - && entry.value.equals(value)) { - return entry.index; - } - entry = entry.next; - } - constantPool.putByte(Symbol.CONSTANT_UTF8_TAG).putUTF8(value); - return put(new Entry(constantPoolCount++, Symbol.CONSTANT_UTF8_TAG, value, hashCode)).index; - } - - /** - * Adds a new CONSTANT_String_info to the constant pool of this symbol table. - * - * @param index the constant pool index of the new Symbol. - * @param value a string. - */ - private void addConstantUtf8(final int index, final String value) { - add(new Entry(index, Symbol.CONSTANT_UTF8_TAG, value, hash(Symbol.CONSTANT_UTF8_TAG, value))); - } - - /** - * Adds a CONSTANT_MethodHandle_info to the constant pool of this symbol table. Does nothing if - * the constant pool already contains a similar item. - * - * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link - * Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link - * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link - * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner the internal name of a class of interface. - * @param name a field or method name. - * @param descriptor a field or method descriptor. - * @param isInterface whether owner is an interface or not. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantMethodHandle( - final int referenceKind, - final String owner, - final String name, - final String descriptor, - final boolean isInterface) { - final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG; - // Note that we don't need to include isInterface in the hash computation, because it is - // redundant with owner (we can't have the same owner with different isInterface values). - int hashCode = hash(tag, owner, name, descriptor, referenceKind); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag - && entry.hashCode == hashCode - && entry.data == referenceKind - && entry.owner.equals(owner) - && entry.name.equals(name) - && entry.value.equals(descriptor)) { - return entry; - } - entry = entry.next; - } - if (referenceKind <= Opcodes.H_PUTSTATIC) { - constantPool.put112(tag, referenceKind, addConstantFieldref(owner, name, descriptor).index); - } else { - constantPool.put112( - tag, referenceKind, addConstantMethodref(owner, name, descriptor, isInterface).index); - } - return put( - new Entry(constantPoolCount++, tag, owner, name, descriptor, referenceKind, hashCode)); - } - - /** - * Adds a new CONSTANT_MethodHandle_info to the constant pool of this symbol table. - * - * @param index the constant pool index of the new Symbol. - * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link - * Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link - * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link - * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner the internal name of a class of interface. - * @param name a field or method name. - * @param descriptor a field or method descriptor. - */ - private void addConstantMethodHandle( - final int index, - final int referenceKind, - final String owner, - final String name, - final String descriptor) { - final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG; - int hashCode = hash(tag, owner, name, descriptor, referenceKind); - add(new Entry(index, tag, owner, name, descriptor, referenceKind, hashCode)); - } - - /** - * Adds a CONSTANT_MethodType_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param methodDescriptor a method descriptor. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantMethodType(final String methodDescriptor) { - return addConstantUtf8Reference(Symbol.CONSTANT_METHOD_TYPE_TAG, methodDescriptor); - } - - /** - * Adds a CONSTANT_Dynamic_info to the constant pool of this symbol table. Also adds the related - * bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the constant - * pool already contains a similar item. - * - * @param name a method name. - * @param descriptor a field descriptor. - * @param bootstrapMethodHandle a bootstrap method handle. - * @param bootstrapMethodArguments the bootstrap method arguments. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantDynamic( - final String name, - final String descriptor, - final Handle bootstrapMethodHandle, - final Object... bootstrapMethodArguments) { - Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments); - return addConstantDynamicOrInvokeDynamicReference( - Symbol.CONSTANT_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index); - } - - /** - * Adds a CONSTANT_InvokeDynamic_info to the constant pool of this symbol table. Also adds the - * related bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param name a method name. - * @param descriptor a method descriptor. - * @param bootstrapMethodHandle a bootstrap method handle. - * @param bootstrapMethodArguments the bootstrap method arguments. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantInvokeDynamic( - final String name, - final String descriptor, - final Handle bootstrapMethodHandle, - final Object... bootstrapMethodArguments) { - Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments); - return addConstantDynamicOrInvokeDynamicReference( - Symbol.CONSTANT_INVOKE_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index); - } - - /** - * Adds a CONSTANT_Dynamic or a CONSTANT_InvokeDynamic_info to the constant pool of this symbol - * table. Does nothing if the constant pool already contains a similar item. - * - * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link - * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}. - * @param name a method name. - * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG) or a method descriptor for - * CONSTANT_INVOKE_DYNAMIC_TAG. - * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute. - * @return a new or already existing Symbol with the given value. - */ - private Symbol addConstantDynamicOrInvokeDynamicReference( - final int tag, final String name, final String descriptor, final int bootstrapMethodIndex) { - int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag - && entry.hashCode == hashCode - && entry.data == bootstrapMethodIndex - && entry.name.equals(name) - && entry.value.equals(descriptor)) { - return entry; - } - entry = entry.next; - } - constantPool.put122(tag, bootstrapMethodIndex, addConstantNameAndType(name, descriptor)); - return put( - new Entry( - constantPoolCount++, tag, null, name, descriptor, bootstrapMethodIndex, hashCode)); - } - - /** - * Adds a new CONSTANT_Dynamic_info or CONSTANT_InvokeDynamic_info to the constant pool of this - * symbol table. - * - * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link - * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}. - * @param index the constant pool index of the new Symbol. - * @param name a method name. - * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG or a method descriptor for - * CONSTANT_INVOKE_DYNAMIC_TAG. - * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute. - */ - private void addConstantDynamicOrInvokeDynamicReference( - final int tag, - final int index, - final String name, - final String descriptor, - final int bootstrapMethodIndex) { - int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex); - add(new Entry(index, tag, null, name, descriptor, bootstrapMethodIndex, hashCode)); - } - - /** - * Adds a CONSTANT_Module_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param moduleName a fully qualified name (using dots) of a module. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantModule(final String moduleName) { - return addConstantUtf8Reference(Symbol.CONSTANT_MODULE_TAG, moduleName); - } - - /** - * Adds a CONSTANT_Package_info to the constant pool of this symbol table. Does nothing if the - * constant pool already contains a similar item. - * - * @param packageName the internal name of a package. - * @return a new or already existing Symbol with the given value. - */ - Symbol addConstantPackage(final String packageName) { - return addConstantUtf8Reference(Symbol.CONSTANT_PACKAGE_TAG, packageName); - } - - /** - * Adds a CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info, - * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. Does - * nothing if the constant pool already contains a similar item. - * - * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link - * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link - * Symbol#CONSTANT_PACKAGE_TAG}. - * @param value an internal class name, an arbitrary string, a method descriptor, a module or a - * package name, depending on tag. - * @return a new or already existing Symbol with the given value. - */ - private Symbol addConstantUtf8Reference(final int tag, final String value) { - int hashCode = hash(tag, value); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == tag && entry.hashCode == hashCode && entry.value.equals(value)) { - return entry; - } - entry = entry.next; - } - constantPool.put12(tag, addConstantUtf8(value)); - return put(new Entry(constantPoolCount++, tag, value, hashCode)); - } - - /** - * Adds a new CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info, - * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. - * - * @param index the constant pool index of the new Symbol. - * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link - * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link - * Symbol#CONSTANT_PACKAGE_TAG}. - * @param value an internal class name, an arbitrary string, a method descriptor, a module or a - * package name, depending on tag. - */ - private void addConstantUtf8Reference(final int index, final int tag, final String value) { - add(new Entry(index, tag, value, hash(tag, value))); - } - - // ----------------------------------------------------------------------------------------------- - // Bootstrap method entries management. - // ----------------------------------------------------------------------------------------------- - - /** - * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if - * the BootstrapMethods already contains a similar bootstrap method. - * - * @param bootstrapMethodHandle a bootstrap method handle. - * @param bootstrapMethodArguments the bootstrap method arguments. - * @return a new or already existing Symbol with the given value. - */ - Symbol addBootstrapMethod( - final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { - ByteVector bootstrapMethodsAttribute = bootstrapMethods; - if (bootstrapMethodsAttribute == null) { - bootstrapMethodsAttribute = bootstrapMethods = new ByteVector(); - } - - // The bootstrap method arguments can be Constant_Dynamic values, which reference other - // bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool - // and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified - // while adding the given bootstrap method to it, in the rest of this method. - for (Object bootstrapMethodArgument : bootstrapMethodArguments) { - addConstant(bootstrapMethodArgument); - } - - // Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to - // compare it with existing ones, and will be reverted below if there is already a similar - // bootstrap method. - int bootstrapMethodOffset = bootstrapMethodsAttribute.length; - bootstrapMethodsAttribute.putShort( - addConstantMethodHandle( - bootstrapMethodHandle.getTag(), - bootstrapMethodHandle.getOwner(), - bootstrapMethodHandle.getName(), - bootstrapMethodHandle.getDesc(), - bootstrapMethodHandle.isInterface()) - .index); - int numBootstrapArguments = bootstrapMethodArguments.length; - bootstrapMethodsAttribute.putShort(numBootstrapArguments); - for (Object bootstrapMethodArgument : bootstrapMethodArguments) { - bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index); - } - - // Compute the length and the hash code of the bootstrap method. - int bootstrapMethodlength = bootstrapMethodsAttribute.length - bootstrapMethodOffset; - int hashCode = bootstrapMethodHandle.hashCode(); - for (Object bootstrapMethodArgument : bootstrapMethodArguments) { - hashCode ^= bootstrapMethodArgument.hashCode(); - } - hashCode &= 0x7FFFFFFF; - - // Add the bootstrap method to the symbol table or revert the above changes. - return addBootstrapMethod(bootstrapMethodOffset, bootstrapMethodlength, hashCode); - } - - /** - * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if - * the BootstrapMethods already contains a similar bootstrap method (more precisely, reverts the - * content of {@link #bootstrapMethods} to remove the last, duplicate bootstrap method). - * - * @param offset the offset of the last bootstrap method in {@link #bootstrapMethods}, in bytes. - * @param length the length of this bootstrap method in {@link #bootstrapMethods}, in bytes. - * @param hashCode the hash code of this bootstrap method. - * @return a new or already existing Symbol with the given value. - */ - private Symbol addBootstrapMethod(final int offset, final int length, final int hashCode) { - final byte[] bootstrapMethodsData = bootstrapMethods.data; - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == Symbol.BOOTSTRAP_METHOD_TAG && entry.hashCode == hashCode) { - int otherOffset = (int) entry.data; - boolean isSameBootstrapMethod = true; - for (int i = 0; i < length; ++i) { - if (bootstrapMethodsData[offset + i] != bootstrapMethodsData[otherOffset + i]) { - isSameBootstrapMethod = false; - break; - } - } - if (isSameBootstrapMethod) { - bootstrapMethods.length = offset; // Revert to old position. - return entry; - } - } - entry = entry.next; - } - return put(new Entry(bootstrapMethodCount++, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode)); - } - - // ----------------------------------------------------------------------------------------------- - // Type table entries management. - // ----------------------------------------------------------------------------------------------- - - /** - * @param typeIndex a type table index. - * @return the type table element whose index is given. - */ - Symbol getType(final int typeIndex) { - return typeTable[typeIndex]; - } - - /** - * Adds a type in the type table of this symbol table. Does nothing if the type table already - * contains a similar type. - * - * @param value an internal class name. - * @return the index of a new or already existing type Symbol with the given value. - */ - int addType(final String value) { - int hashCode = hash(Symbol.TYPE_TAG, value); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == Symbol.TYPE_TAG && entry.hashCode == hashCode && entry.value.equals(value)) { - return entry.index; - } - entry = entry.next; - } - return addType(new Entry(typeCount, Symbol.TYPE_TAG, value, hashCode)); - } - - /** - * Adds an {@link Frame#ITEM_UNINITIALIZED} type in the type table of this symbol table. Does - * nothing if the type table already contains a similar type. - * - * @param value an internal class name. - * @param bytecodeOffset the bytecode offset of the NEW instruction that created this {@link - * Frame#ITEM_UNINITIALIZED} type value. - * @return the index of a new or already existing type Symbol with the given value. - */ - int addUninitializedType(final String value, final int bytecodeOffset) { - int hashCode = hash(Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == Symbol.UNINITIALIZED_TYPE_TAG - && entry.hashCode == hashCode - && entry.data == bytecodeOffset - && entry.value.equals(value)) { - return entry.index; - } - entry = entry.next; - } - return addType( - new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode)); - } - - /** - * Adds a merged type in the type table of this symbol table. Does nothing if the type table - * already contains a similar type. - * - * @param typeTableIndex1 a {@link Symbol#TYPE_TAG} type, specified by its index in the type - * table. - * @param typeTableIndex2 another {@link Symbol#TYPE_TAG} type, specified by its index in the type - * table. - * @return the index of a new or already existing {@link Symbol#TYPE_TAG} type Symbol, - * corresponding to the common super class of the given types. - */ - int addMergedType(final int typeTableIndex1, final int typeTableIndex2) { - // TODO sort the arguments? The merge result should be independent of their order. - long data = typeTableIndex1 | (((long) typeTableIndex2) << 32); - int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2); - Entry entry = get(hashCode); - while (entry != null) { - if (entry.tag == Symbol.MERGED_TYPE_TAG && entry.hashCode == hashCode && entry.data == data) { - return entry.info; - } - entry = entry.next; - } - String type1 = typeTable[typeTableIndex1].value; - String type2 = typeTable[typeTableIndex2].value; - int commonSuperTypeIndex = addType(classWriter.getCommonSuperClass(type1, type2)); - put(new Entry(typeCount, Symbol.MERGED_TYPE_TAG, data, hashCode)).info = commonSuperTypeIndex; - return commonSuperTypeIndex; - } - - /** - * Adds the given type Symbol to {@link #typeTable}. - * - * @param entry a {@link Symbol#TYPE_TAG} or {@link Symbol#UNINITIALIZED_TYPE_TAG} type symbol. - * The index of this Symbol must be equal to the current value of {@link #typeCount}. - * @return the index in {@link #typeTable} where the given type was added, which is also equal to - * entry's index by hypothesis. - */ - private int addType(final Entry entry) { - if (typeTable == null) { - typeTable = new Entry[16]; - } - if (typeCount == typeTable.length) { - Entry[] newTypeTable = new Entry[2 * typeTable.length]; - System.arraycopy(typeTable, 0, newTypeTable, 0, typeTable.length); - typeTable = newTypeTable; - } - typeTable[typeCount++] = entry; - return put(entry).index; - } - - // ----------------------------------------------------------------------------------------------- - // Static helper methods to compute hash codes. - // ----------------------------------------------------------------------------------------------- - - private static int hash(final int tag, final int value) { - return 0x7FFFFFFF & (tag + value); - } - - private static int hash(final int tag, final long value) { - return 0x7FFFFFFF & (tag + (int) value + (int) (value >>> 32)); - } - - private static int hash(final int tag, final String value) { - return 0x7FFFFFFF & (tag + value.hashCode()); - } - - private static int hash(final int tag, final String value1, final int value2) { - return 0x7FFFFFFF & (tag + value1.hashCode() + value2); - } - - private static int hash(final int tag, final String value1, final String value2) { - return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode()); - } - - private static int hash( - final int tag, final String value1, final String value2, final int value3) { - return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * (value3 + 1)); - } - - private static int hash( - final int tag, final String value1, final String value2, final String value3) { - return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode()); - } - - private static int hash( - final int tag, - final String value1, - final String value2, - final String value3, - final int value4) { - return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode() * value4); - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Type.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Type.java deleted file mode 100644 index 9ee5a396ce..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/Type.java +++ /dev/null @@ -1,906 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package bsh.org.objectweb.asm; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -/** - * A Java field or method type. This class can be used to make it easier to manipulate type and - * method descriptors. - * - * @author Eric Bruneton - * @author Chris Nokleberg - */ -public class Type { - - /** The sort of the void type. See {@link #getSort}. */ - public static final int VOID = 0; - - /** The sort of the boolean type. See {@link #getSort}. */ - public static final int BOOLEAN = 1; - - /** The sort of the char type. See {@link #getSort}. */ - public static final int CHAR = 2; - - /** The sort of the byte type. See {@link #getSort}. */ - public static final int BYTE = 3; - - /** The sort of the short type. See {@link #getSort}. */ - public static final int SHORT = 4; - - /** The sort of the int type. See {@link #getSort}. */ - public static final int INT = 5; - - /** The sort of the float type. See {@link #getSort}. */ - public static final int FLOAT = 6; - - /** The sort of the long type. See {@link #getSort}. */ - public static final int LONG = 7; - - /** The sort of the double type. See {@link #getSort}. */ - public static final int DOUBLE = 8; - - /** The sort of array reference types. See {@link #getSort}. */ - public static final int ARRAY = 9; - - /** The sort of object reference types. See {@link #getSort}. */ - public static final int OBJECT = 10; - - /** The sort of method types. See {@link #getSort}. */ - public static final int METHOD = 11; - - /** The (private) sort of object reference types represented with an internal name. */ - private static final int INTERNAL = 12; - - /** The descriptors of the primitive types. */ - private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD"; - - /** The void type. */ - public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1); - - /** The boolean type. */ - public static final Type BOOLEAN_TYPE = - new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1); - - /** The char type. */ - public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1); - - /** The byte type. */ - public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1); - - /** The short type. */ - public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1); - - /** The int type. */ - public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1); - - /** The float type. */ - public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1); - - /** The long type. */ - public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1); - - /** The double type. */ - public static final Type DOUBLE_TYPE = - new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1); - - // ----------------------------------------------------------------------------------------------- - // Fields - // ----------------------------------------------------------------------------------------------- - - /** - * The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, - * {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, - * {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}. - */ - private final int sort; - - /** - * A buffer containing the value of this field or method type. This value is an internal name for - * {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other - * cases. - * - *

For {@link #OBJECT} types, this field also contains the descriptor: the characters in - * [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link - * #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor. - */ - private final String valueBuffer; - - /** - * The beginning index, inclusive, of the value of this Java field or method type in {@link - * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, - * and a field or method descriptor in the other cases. - */ - private final int valueBegin; - - /** - * The end index, exclusive, of the value of this Java field or method type in {@link - * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, - * and a field or method descriptor in the other cases. - */ - private final int valueEnd; - - // ----------------------------------------------------------------------------------------------- - // Constructors - // ----------------------------------------------------------------------------------------------- - - /** - * Constructs a reference type. - * - * @param sort the sort of this type, see {@link #sort}. - * @param valueBuffer a buffer containing the value of this field or method type. - * @param valueBegin the beginning index, inclusive, of the value of this field or method type in - * valueBuffer. - * @param valueEnd tne end index, exclusive, of the value of this field or method type in - * valueBuffer. - */ - private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) { - this.sort = sort; - this.valueBuffer = valueBuffer; - this.valueBegin = valueBegin; - this.valueEnd = valueEnd; - } - - /** - * Returns the {@link Type} corresponding to the given type descriptor. - * - * @param typeDescriptor a field or method type descriptor. - * @return the {@link Type} corresponding to the given type descriptor. - */ - public static Type getType(final String typeDescriptor) { - return getType(typeDescriptor, 0, typeDescriptor.length()); - } - - /** - * Returns the {@link Type} corresponding to the given internal name. - * - * @param internalName an internal name. - * @return the {@link Type} corresponding to the given internal name. - */ - public static Type getObjectType(final String internalName) { - return new Type( - internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length()); - } - - /** - * Returns the {@link Type} corresponding to the given method descriptor. Equivalent to - * Type.getType(methodDescriptor). - * - * @param methodDescriptor a method descriptor. - * @return the {@link Type} corresponding to the given method descriptor. - */ - public static Type getMethodType(final String methodDescriptor) { - return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length()); - } - - /** - * Returns the method {@link Type} corresponding to the given argument and return types. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the method {@link Type} corresponding to the given argument and return types. - */ - public static Type getMethodType(final Type returnType, final Type... argumentTypes) { - return getType(getMethodDescriptor(returnType, argumentTypes)); - } - - /** - * Returns the {@link Type} corresponding to the given class. - * - * @param clazz a class. - * @return the {@link Type} corresponding to the given class. - */ - public static Type getType(final Class clazz) { - if (clazz.isPrimitive()) { - if (clazz == Integer.TYPE) { - return INT_TYPE; - } else if (clazz == Void.TYPE) { - return VOID_TYPE; - } else if (clazz == Boolean.TYPE) { - return BOOLEAN_TYPE; - } else if (clazz == Byte.TYPE) { - return BYTE_TYPE; - } else if (clazz == Character.TYPE) { - return CHAR_TYPE; - } else if (clazz == Short.TYPE) { - return SHORT_TYPE; - } else if (clazz == Double.TYPE) { - return DOUBLE_TYPE; - } else if (clazz == Float.TYPE) { - return FLOAT_TYPE; - } else if (clazz == Long.TYPE) { - return LONG_TYPE; - } else { - throw new AssertionError(); - } - } else { - return getType(getDescriptor(clazz)); - } - } - - /** - * Returns the method {@link Type} corresponding to the given constructor. - * - * @param constructor a {@link Constructor} object. - * @return the method {@link Type} corresponding to the given constructor. - */ - public static Type getType(final Constructor constructor) { - return getType(getConstructorDescriptor(constructor)); - } - - /** - * Returns the method {@link Type} corresponding to the given method. - * - * @param method a {@link Method} object. - * @return the method {@link Type} corresponding to the given method. - */ - public static Type getType(final Method method) { - return getType(getMethodDescriptor(method)); - } - - /** - * Returns the {@link Type} values corresponding to the argument types of the given method - * descriptor. - * - * @param methodDescriptor a method descriptor. - * @return the {@link Type} values corresponding to the argument types of the given method - * descriptor. - */ - public static Type[] getArgumentTypes(final String methodDescriptor) { - // First step: compute the number of argument types in methodDescriptor. - int numArgumentTypes = 0; - // Skip the first character, which is always a '('. - int currentOffset = 1; - // Parse the argument types, one at a each loop iteration. - while (methodDescriptor.charAt(currentOffset) != ')') { - while (methodDescriptor.charAt(currentOffset) == '[') { - currentOffset++; - } - if (methodDescriptor.charAt(currentOffset++) == 'L') { - while (methodDescriptor.charAt(currentOffset++) != ';') { - // Skip the argument descriptor content. - } - } - ++numArgumentTypes; - } - - // Second step: create a Type instance for each argument type. - Type[] argumentTypes = new Type[numArgumentTypes]; - // Skip the first character, which is always a '('. - currentOffset = 1; - // Parse and create the argument types, one at each loop iteration. - int currentArgumentTypeIndex = 0; - while (methodDescriptor.charAt(currentOffset) != ')') { - final int currentArgumentTypeOffset = currentOffset; - while (methodDescriptor.charAt(currentOffset) == '[') { - currentOffset++; - } - if (methodDescriptor.charAt(currentOffset++) == 'L') { - while (methodDescriptor.charAt(currentOffset++) != ';') { - // Skip the argument descriptor content. - } - } - argumentTypes[currentArgumentTypeIndex++] = - getType(methodDescriptor, currentArgumentTypeOffset, currentOffset); - } - return argumentTypes; - } - - /** - * Returns the {@link Type} values corresponding to the argument types of the given method. - * - * @param method a method. - * @return the {@link Type} values corresponding to the argument types of the given method. - */ - public static Type[] getArgumentTypes(final Method method) { - Class[] classes = method.getParameterTypes(); - Type[] types = new Type[classes.length]; - for (int i = classes.length - 1; i >= 0; --i) { - types[i] = getType(classes[i]); - } - return types; - } - - /** - * Returns the {@link Type} corresponding to the return type of the given method descriptor. - * - * @param methodDescriptor a method descriptor. - * @return the {@link Type} corresponding to the return type of the given method descriptor. - */ - public static Type getReturnType(final String methodDescriptor) { - // Skip the first character, which is always a '('. - int currentOffset = 1; - // Skip the argument types, one at a each loop iteration. - while (methodDescriptor.charAt(currentOffset) != ')') { - while (methodDescriptor.charAt(currentOffset) == '[') { - currentOffset++; - } - if (methodDescriptor.charAt(currentOffset++) == 'L') { - while (methodDescriptor.charAt(currentOffset++) != ';') { - // Skip the argument descriptor content. - } - } - } - return getType(methodDescriptor, currentOffset + 1, methodDescriptor.length()); - } - - /** - * Returns the {@link Type} corresponding to the return type of the given method. - * - * @param method a method. - * @return the {@link Type} corresponding to the return type of the given method. - */ - public static Type getReturnType(final Method method) { - return getType(method.getReturnType()); - } - - /** - * Computes the size of the arguments and of the return value of a method. - * - * @param methodDescriptor a method descriptor. - * @return the size of the arguments of the method (plus one for the implicit this argument), - * argumentsSize, and the size of its return value, returnSize, packed into a single int i = - * (argumentsSize << 2) | returnSize (argumentsSize is therefore equal to i - * >> 2, and returnSize to i & 0x03). - */ - public static int getArgumentsAndReturnSizes(final String methodDescriptor) { - int argumentsSize = 1; - // Skip the first character, which is always a '('. - int currentOffset = 1; - int currentChar = methodDescriptor.charAt(currentOffset); - // Parse the argument types and compute their size, one at a each loop iteration. - while (currentChar != ')') { - if (currentChar == 'J' || currentChar == 'D') { - currentOffset++; - argumentsSize += 2; - } else { - while (methodDescriptor.charAt(currentOffset) == '[') { - currentOffset++; - } - if (methodDescriptor.charAt(currentOffset++) == 'L') { - while (methodDescriptor.charAt(currentOffset++) != ';') { - // Skip the argument descriptor content. - } - } - argumentsSize += 1; - } - currentChar = methodDescriptor.charAt(currentOffset); - } - currentChar = methodDescriptor.charAt(currentOffset + 1); - if (currentChar == 'V') { - return argumentsSize << 2; - } else { - int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1; - return argumentsSize << 2 | returnSize; - } - } - - /** - * Returns the {@link Type} corresponding to the given field or method descriptor. - * - * @param descriptorBuffer a buffer containing the field or method descriptor. - * @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in - * descriptorBuffer. - * @param descriptorEnd the end index, exclusive, of the field or method descriptor in - * descriptorBuffer. - * @return the {@link Type} corresponding to the given type descriptor. - */ - private static Type getType( - final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) { - switch (descriptorBuffer.charAt(descriptorBegin)) { - case 'V': - return VOID_TYPE; - case 'Z': - return BOOLEAN_TYPE; - case 'C': - return CHAR_TYPE; - case 'B': - return BYTE_TYPE; - case 'S': - return SHORT_TYPE; - case 'I': - return INT_TYPE; - case 'F': - return FLOAT_TYPE; - case 'J': - return LONG_TYPE; - case 'D': - return DOUBLE_TYPE; - case '[': - return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd); - case 'L': - return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1); - case '(': - return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd); - default: - throw new IllegalArgumentException(); - } - } - - // ----------------------------------------------------------------------------------------------- - // Accessors - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the sort of this type. - * - * @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link - * #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or - * {@link #METHOD}. - */ - public int getSort() { - return sort == INTERNAL ? OBJECT : sort; - } - - /** - * Returns the number of dimensions of this array type. This method should only be used for an - * array type. - * - * @return the number of dimensions of this array type. - */ - public int getDimensions() { - int numDimensions = 1; - while (valueBuffer.charAt(valueBegin + numDimensions) == '[') { - numDimensions++; - } - return numDimensions; - } - - /** - * Returns the type of the elements of this array type. This method should only be used for an - * array type. - * - * @return Returns the type of the elements of this array type. - */ - public Type getElementType() { - final int numDimensions = getDimensions(); - return getType(valueBuffer, valueBegin + numDimensions, valueEnd); - } - - /** - * Returns the binary name of the class corresponding to this type. This method must not be used - * on method types. - * - * @return the binary name of the class corresponding to this type. - */ - public String getClassName() { - switch (sort) { - case VOID: - return "void"; - case BOOLEAN: - return "boolean"; - case CHAR: - return "char"; - case BYTE: - return "byte"; - case SHORT: - return "short"; - case INT: - return "int"; - case FLOAT: - return "float"; - case LONG: - return "long"; - case DOUBLE: - return "double"; - case ARRAY: - StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName()); - for (int i = getDimensions(); i > 0; --i) { - stringBuilder.append("[]"); - } - return stringBuilder.toString(); - case OBJECT: - case INTERNAL: - return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.'); - default: - throw new AssertionError(); - } - } - - /** - * Returns the internal name of the class corresponding to this object or array type. The internal - * name of a class is its fully qualified name (as returned by Class.getName(), where '.' are - * replaced by '/'). This method should only be used for an object or array type. - * - * @return the internal name of the class corresponding to this object type. - */ - public String getInternalName() { - return valueBuffer.substring(valueBegin, valueEnd); - } - - /** - * Returns the argument types of methods of this type. This method should only be used for method - * types. - * - * @return the argument types of methods of this type. - */ - public Type[] getArgumentTypes() { - return getArgumentTypes(getDescriptor()); - } - - /** - * Returns the return type of methods of this type. This method should only be used for method - * types. - * - * @return the return type of methods of this type. - */ - public Type getReturnType() { - return getReturnType(getDescriptor()); - } - - /** - * Returns the size of the arguments and of the return value of methods of this type. This method - * should only be used for method types. - * - * @return the size of the arguments of the method (plus one for the implicit this argument), - * argumentsSize, and the size of its return value, returnSize, packed into a single int i = - * (argumentsSize << 2) | returnSize (argumentsSize is therefore equal to i - * >> 2, and returnSize to i & 0x03). - */ - public int getArgumentsAndReturnSizes() { - return getArgumentsAndReturnSizes(getDescriptor()); - } - - // ----------------------------------------------------------------------------------------------- - // Conversion to type descriptors - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the descriptor corresponding to this type. - * - * @return the descriptor corresponding to this type. - */ - public String getDescriptor() { - if (sort == OBJECT) { - return valueBuffer.substring(valueBegin - 1, valueEnd + 1); - } else if (sort == INTERNAL) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append('L'); - stringBuilder.append(valueBuffer, valueBegin, valueEnd); - stringBuilder.append(';'); - return stringBuilder.toString(); - } else { - return valueBuffer.substring(valueBegin, valueEnd); - } - } - - /** - * Returns the descriptor corresponding to the given argument and return types. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the descriptor corresponding to the given argument and return types. - */ - public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append('('); - for (int i = 0; i < argumentTypes.length; ++i) { - argumentTypes[i].appendDescriptor(stringBuilder); - } - stringBuilder.append(')'); - returnType.appendDescriptor(stringBuilder); - return stringBuilder.toString(); - } - - /** - * Appends the descriptor corresponding to this type to the given string buffer. - * - * @param stringBuilder the string builder to which the descriptor must be appended. - */ - private void appendDescriptor(final StringBuilder stringBuilder) { - if (sort == OBJECT) { - stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1); - } else if (sort == INTERNAL) { - stringBuilder.append('L'); - stringBuilder.append(valueBuffer, valueBegin, valueEnd); - stringBuilder.append(';'); - } else { - stringBuilder.append(valueBuffer, valueBegin, valueEnd); - } - } - - // ----------------------------------------------------------------------------------------------- - // Direct conversion from classes to type descriptors, - // without intermediate Type objects - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the internal name of the given class. The internal name of a class is its fully - * qualified name, as returned by Class.getName(), where '.' are replaced by '/'. - * - * @param clazz an object or array class. - * @return the internal name of the given class. - */ - public static String getInternalName(final Class clazz) { - return clazz.getName().replace('.', '/'); - } - - /** - * Returns the descriptor corresponding to the given class. - * - * @param clazz an object class, a primitive class or an array class. - * @return the descriptor corresponding to the given class. - */ - public static String getDescriptor(final Class clazz) { - StringBuilder stringBuilder = new StringBuilder(); - appendDescriptor(stringBuilder, clazz); - return stringBuilder.toString(); - } - - /** - * Returns the descriptor corresponding to the given constructor. - * - * @param constructor a {@link Constructor} object. - * @return the descriptor of the given constructor. - */ - public static String getConstructorDescriptor(final Constructor constructor) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append('('); - Class[] parameters = constructor.getParameterTypes(); - for (int i = 0; i < parameters.length; ++i) { - appendDescriptor(stringBuilder, parameters[i]); - } - return stringBuilder.append(")V").toString(); - } - - /** - * Returns the descriptor corresponding to the given method. - * - * @param method a {@link Method} object. - * @return the descriptor of the given method. - */ - public static String getMethodDescriptor(final Method method) { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append('('); - Class[] parameters = method.getParameterTypes(); - for (int i = 0; i < parameters.length; ++i) { - appendDescriptor(stringBuilder, parameters[i]); - } - stringBuilder.append(')'); - appendDescriptor(stringBuilder, method.getReturnType()); - return stringBuilder.toString(); - } - - /** - * Appends the descriptor of the given class to the given string builder. - * - * @param stringBuilder the string builder to which the descriptor must be appended. - * @param clazz the class whose descriptor must be computed. - */ - private static void appendDescriptor(final StringBuilder stringBuilder, final Class clazz) { - Class currentClass = clazz; - while (currentClass.isArray()) { - stringBuilder.append('['); - currentClass = currentClass.getComponentType(); - } - if (currentClass.isPrimitive()) { - char descriptor; - if (currentClass == Integer.TYPE) { - descriptor = 'I'; - } else if (currentClass == Void.TYPE) { - descriptor = 'V'; - } else if (currentClass == Boolean.TYPE) { - descriptor = 'Z'; - } else if (currentClass == Byte.TYPE) { - descriptor = 'B'; - } else if (currentClass == Character.TYPE) { - descriptor = 'C'; - } else if (currentClass == Short.TYPE) { - descriptor = 'S'; - } else if (currentClass == Double.TYPE) { - descriptor = 'D'; - } else if (currentClass == Float.TYPE) { - descriptor = 'F'; - } else if (currentClass == Long.TYPE) { - descriptor = 'J'; - } else { - throw new AssertionError(); - } - stringBuilder.append(descriptor); - } else { - stringBuilder.append('L'); - String name = currentClass.getName(); - int nameLength = name.length(); - for (int i = 0; i < nameLength; ++i) { - char car = name.charAt(i); - stringBuilder.append(car == '.' ? '/' : car); - } - stringBuilder.append(';'); - } - } - - // ----------------------------------------------------------------------------------------------- - // Corresponding size and opcodes - // ----------------------------------------------------------------------------------------------- - - /** - * Returns the size of values of this type. This method must not be used for method types. - * - * @return the size of values of this type, i.e., 2 for long and double, 0 for - * void and 1 otherwise. - */ - public int getSize() { - switch (sort) { - case VOID: - return 0; - case BOOLEAN: - case CHAR: - case BYTE: - case SHORT: - case INT: - case FLOAT: - case ARRAY: - case OBJECT: - case INTERNAL: - return 1; - case LONG: - case DOUBLE: - return 2; - default: - throw new AssertionError(); - } - } - - /** - * Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for - * method types. - * - * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, - * IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and - * IRETURN. - * @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For - * example, if this type is float and opcode is IRETURN, this method returns - * FRETURN. - */ - public int getOpcode(final int opcode) { - if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { - switch (sort) { - case BOOLEAN: - case BYTE: - return opcode + (Opcodes.BALOAD - Opcodes.IALOAD); - case CHAR: - return opcode + (Opcodes.CALOAD - Opcodes.IALOAD); - case SHORT: - return opcode + (Opcodes.SALOAD - Opcodes.IALOAD); - case INT: - return opcode; - case FLOAT: - return opcode + (Opcodes.FALOAD - Opcodes.IALOAD); - case LONG: - return opcode + (Opcodes.LALOAD - Opcodes.IALOAD); - case DOUBLE: - return opcode + (Opcodes.DALOAD - Opcodes.IALOAD); - case ARRAY: - case OBJECT: - case INTERNAL: - return opcode + (Opcodes.AALOAD - Opcodes.IALOAD); - case METHOD: - case VOID: - throw new UnsupportedOperationException(); - default: - throw new AssertionError(); - } - } else { - switch (sort) { - case VOID: - if (opcode != Opcodes.IRETURN) { - throw new UnsupportedOperationException(); - } - return Opcodes.RETURN; - case BOOLEAN: - case BYTE: - case CHAR: - case SHORT: - case INT: - return opcode; - case FLOAT: - return opcode + (Opcodes.FRETURN - Opcodes.IRETURN); - case LONG: - return opcode + (Opcodes.LRETURN - Opcodes.IRETURN); - case DOUBLE: - return opcode + (Opcodes.DRETURN - Opcodes.IRETURN); - case ARRAY: - case OBJECT: - case INTERNAL: - if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) { - throw new UnsupportedOperationException(); - } - return opcode + (Opcodes.ARETURN - Opcodes.IRETURN); - case METHOD: - throw new UnsupportedOperationException(); - default: - throw new AssertionError(); - } - } - } - - // ----------------------------------------------------------------------------------------------- - // Equals, hashCode and toString - // ----------------------------------------------------------------------------------------------- - - /** - * Tests if the given object is equal to this type. - * - * @param object the object to be compared to this type. - * @return true if the given object is equal to this type. - */ - @Override - public boolean equals(final Object object) { - if (this == object) { - return true; - } - if (!(object instanceof Type)) { - return false; - } - Type other = (Type) object; - if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) { - return false; - } - int begin = valueBegin; - int end = valueEnd; - int otherBegin = other.valueBegin; - int otherEnd = other.valueEnd; - // Compare the values. - if (end - begin != otherEnd - otherBegin) { - return false; - } - for (int i = begin, j = otherBegin; i < end; i++, j++) { - if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) { - return false; - } - } - return true; - } - - /** - * Returns a hash code value for this type. - * - * @return a hash code value for this type. - */ - @Override - public int hashCode() { - int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort); - if (sort >= ARRAY) { - for (int i = valueBegin, end = valueEnd; i < end; i++) { - hashCode = 17 * (hashCode + valueBuffer.charAt(i)); - } - } - return hashCode; - } - - /** - * Returns a string representation of this type. - * - * @return the descriptor of this type. - */ - @Override - public String toString() { - return getDescriptor(); - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypePath.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypePath.java deleted file mode 100644 index acae12c8e7..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypePath.java +++ /dev/null @@ -1,201 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. - -package bsh.org.objectweb.asm; - -/** - * The path to a type argument, wildcard bound, array element type, or static inner type within an - * enclosing type. - * - * @author Eric Bruneton - */ -public class TypePath { - - /** A type path step that steps into the element type of an array type. See {@link #getStep}. */ - public static final int ARRAY_ELEMENT = 0; - - /** A type path step that steps into the nested type of a class type. See {@link #getStep}. */ - public static final int INNER_TYPE = 1; - - /** A type path step that steps into the bound of a wildcard type. See {@link #getStep}. */ - public static final int WILDCARD_BOUND = 2; - - /** A type path step that steps into a type argument of a generic type. See {@link #getStep}. */ - public static final int TYPE_ARGUMENT = 3; - - /** - * The byte array where the 'type_path' structure - as defined in the Java Virtual Machine - * Specification (JVMS) - corresponding to this TypePath is stored. The first byte of the - * structure in this array is given by {@link #typePathOffset}. - * - * @see JVMS - * 4.7.20.2 - */ - private final byte[] typePathContainer; - - /** The offset of the first byte of the type_path JVMS structure in {@link #typePathContainer}. */ - private final int typePathOffset; - - /** - * Constructs a new TypePath. - * - * @param typePathContainer a byte array containing a type_path JVMS structure. - * @param typePathOffset the offset of the first byte of the type_path structure in - * typePathContainer. - */ - TypePath(final byte[] typePathContainer, final int typePathOffset) { - this.typePathContainer = typePathContainer; - this.typePathOffset = typePathOffset; - } - - /** - * Returns the length of this path, i.e. its number of steps. - * - * @return the length of this path. - */ - public int getLength() { - // path_length is stored in the first byte of a type_path. - return typePathContainer[typePathOffset]; - } - - /** - * Returns the value of the given step of this path. - * - * @param index an index between 0 and {@link #getLength()}, exclusive. - * @return one of {@link #ARRAY_ELEMENT}, {@link #INNER_TYPE}, {@link #WILDCARD_BOUND}, or {@link - * #TYPE_ARGUMENT}. - */ - public int getStep(final int index) { - // Returns the type_path_kind of the path element of the given index. - return typePathContainer[typePathOffset + 2 * index + 1]; - } - - /** - * Returns the index of the type argument that the given step is stepping into. This method should - * only be used for steps whose value is {@link #TYPE_ARGUMENT}. - * - * @param index an index between 0 and {@link #getLength()}, exclusive. - * @return the index of the type argument that the given step is stepping into. - */ - public int getStepArgument(final int index) { - // Returns the type_argument_index of the path element of the given index. - return typePathContainer[typePathOffset + 2 * index + 2]; - } - - /** - * Converts a type path in string form, in the format used by {@link #toString()}, into a TypePath - * object. - * - * @param typePath a type path in string form, in the format used by {@link #toString()}. May be - * null or empty. - * @return the corresponding TypePath object, or null if the path is empty. - */ - public static TypePath fromString(final String typePath) { - if (typePath == null || typePath.length() == 0) { - return null; - } - int typePathLength = typePath.length(); - ByteVector output = new ByteVector(typePathLength); - output.putByte(0); - int typePathIndex = 0; - while (typePathIndex < typePathLength) { - char c = typePath.charAt(typePathIndex++); - if (c == '[') { - output.put11(ARRAY_ELEMENT, 0); - } else if (c == '.') { - output.put11(INNER_TYPE, 0); - } else if (c == '*') { - output.put11(WILDCARD_BOUND, 0); - } else if (c >= '0' && c <= '9') { - int typeArg = c - '0'; - while (typePathIndex < typePathLength) { - c = typePath.charAt(typePathIndex++); - if (c >= '0' && c <= '9') { - typeArg = typeArg * 10 + c - '0'; - } else if (c == ';') { - break; - } else { - throw new IllegalArgumentException(); - } - } - output.put11(TYPE_ARGUMENT, typeArg); - } else { - throw new IllegalArgumentException(); - } - } - output.data[0] = (byte) (output.length / 2); - return new TypePath(output.data, 0); - } - - /** - * Returns a string representation of this type path. {@link #ARRAY_ELEMENT} steps are represented - * with '[', {@link #INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND} steps with '*' and {@link - * #TYPE_ARGUMENT} steps with their type argument index in decimal form followed by ';'. - */ - @Override - public String toString() { - int length = getLength(); - StringBuilder result = new StringBuilder(length * 2); - for (int i = 0; i < length; ++i) { - switch (getStep(i)) { - case ARRAY_ELEMENT: - result.append('['); - break; - case INNER_TYPE: - result.append('.'); - break; - case WILDCARD_BOUND: - result.append('*'); - break; - case TYPE_ARGUMENT: - result.append(getStepArgument(i)).append(';'); - break; - default: - throw new AssertionError(); - } - } - return result.toString(); - } - - /** - * Puts the type_path JVMS structure corresponding to the given TypePath into the given - * ByteVector. - * - * @param typePath a TypePath instance, or null for empty paths. - * @param output where the type path must be put. - */ - static void put(final TypePath typePath, final ByteVector output) { - if (typePath == null) { - output.putByte(0); - } else { - int length = typePath.typePathContainer[typePath.typePathOffset] * 2 + 1; - output.putByteArray(typePath.typePathContainer, typePath.typePathOffset, length); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypeReference.java b/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypeReference.java deleted file mode 100644 index 293fecc702..0000000000 --- a/scripts/cn1playground/common/src/main/java/bsh/org/objectweb/asm/TypeReference.java +++ /dev/null @@ -1,436 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. - -package bsh.org.objectweb.asm; - -/** - * A reference to a type appearing in a class, field or method declaration, or on an instruction. - * Such a reference designates the part of the class where the referenced type is appearing (e.g. an - * 'extends', 'implements' or 'throws' clause, a 'new' instruction, a 'catch' clause, a type cast, a - * local variable declaration, etc). - * - * @author Eric Bruneton - */ -public class TypeReference { - - /** - * The sort of type references that target a type parameter of a generic class. See {@link - * #getSort}. - */ - public static final int CLASS_TYPE_PARAMETER = 0x00; - - /** - * The sort of type references that target a type parameter of a generic method. See {@link - * #getSort}. - */ - public static final int METHOD_TYPE_PARAMETER = 0x01; - - /** - * The sort of type references that target the super class of a class or one of the interfaces it - * implements. See {@link #getSort}. - */ - public static final int CLASS_EXTENDS = 0x10; - - /** - * The sort of type references that target a bound of a type parameter of a generic class. See - * {@link #getSort}. - */ - public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11; - - /** - * The sort of type references that target a bound of a type parameter of a generic method. See - * {@link #getSort}. - */ - public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12; - - /** The sort of type references that target the type of a field. See {@link #getSort}. */ - public static final int FIELD = 0x13; - - /** The sort of type references that target the return type of a method. See {@link #getSort}. */ - public static final int METHOD_RETURN = 0x14; - - /** - * The sort of type references that target the receiver type of a method. See {@link #getSort}. - */ - public static final int METHOD_RECEIVER = 0x15; - - /** - * The sort of type references that target the type of a formal parameter of a method. See {@link - * #getSort}. - */ - public static final int METHOD_FORMAL_PARAMETER = 0x16; - - /** - * The sort of type references that target the type of an exception declared in the throws clause - * of a method. See {@link #getSort}. - */ - public static final int THROWS = 0x17; - - /** - * The sort of type references that target the type of a local variable in a method. See {@link - * #getSort}. - */ - public static final int LOCAL_VARIABLE = 0x40; - - /** - * The sort of type references that target the type of a resource variable in a method. See {@link - * #getSort}. - */ - public static final int RESOURCE_VARIABLE = 0x41; - - /** - * The sort of type references that target the type of the exception of a 'catch' clause in a - * method. See {@link #getSort}. - */ - public static final int EXCEPTION_PARAMETER = 0x42; - - /** - * The sort of type references that target the type declared in an 'instanceof' instruction. See - * {@link #getSort}. - */ - public static final int INSTANCEOF = 0x43; - - /** - * The sort of type references that target the type of the object created by a 'new' instruction. - * See {@link #getSort}. - */ - public static final int NEW = 0x44; - - /** - * The sort of type references that target the receiver type of a constructor reference. See - * {@link #getSort}. - */ - public static final int CONSTRUCTOR_REFERENCE = 0x45; - - /** - * The sort of type references that target the receiver type of a method reference. See {@link - * #getSort}. - */ - public static final int METHOD_REFERENCE = 0x46; - - /** - * The sort of type references that target the type declared in an explicit or implicit cast - * instruction. See {@link #getSort}. - */ - public static final int CAST = 0x47; - - /** - * The sort of type references that target a type parameter of a generic constructor in a - * constructor call. See {@link #getSort}. - */ - public static final int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; - - /** - * The sort of type references that target a type parameter of a generic method in a method call. - * See {@link #getSort}. - */ - public static final int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; - - /** - * The sort of type references that target a type parameter of a generic constructor in a - * constructor reference. See {@link #getSort}. - */ - public static final int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; - - /** - * The sort of type references that target a type parameter of a generic method in a method - * reference. See {@link #getSort}. - */ - public static final int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; - - /** - * The target_type and target_info structures - as defined in the Java Virtual Machine - * Specification (JVMS) - corresponding to this type reference. target_type uses one byte, and all - * the target_info union fields use up to 3 bytes (except localvar_target, handled with the - * specific method {@link MethodVisitor#visitLocalVariableAnnotation}). Thus, both structures can - * be stored in an int. - * - *

This int field stores target_type (called the TypeReference 'sort' in the public API of this - * class) in its most significant byte, followed by the target_info fields. Depending on - * target_type, 1, 2 or even 3 least significant bytes of this field are unused. target_info - * fields which reference bytecode offsets are set to 0 (these offsets are ignored in ClassReader, - * and recomputed in MethodWriter). - * - * @see JVMS - * 4.7.20 - * @see JVMS - * 4.7.20.1 - */ - private final int targetTypeAndInfo; - - /** - * Constructs a new TypeReference. - * - * @param typeRef the int encoded value of the type reference, as received in a visit method - * related to type annotations, such as {@link ClassVisitor#visitTypeAnnotation}. - */ - public TypeReference(final int typeRef) { - this.targetTypeAndInfo = typeRef; - } - - /** - * Returns a type reference of the given sort. - * - * @param sort one of {@link #FIELD}, {@link #METHOD_RETURN}, {@link #METHOD_RECEIVER}, {@link - * #LOCAL_VARIABLE}, {@link #RESOURCE_VARIABLE}, {@link #INSTANCEOF}, {@link #NEW}, {@link - * #CONSTRUCTOR_REFERENCE}, or {@link #METHOD_REFERENCE}. - * @return a type reference of the given sort. - */ - public static TypeReference newTypeReference(final int sort) { - return new TypeReference(sort << 24); - } - - /** - * Returns a reference to a type parameter of a generic class or method. - * - * @param sort one of {@link #CLASS_TYPE_PARAMETER} or {@link #METHOD_TYPE_PARAMETER}. - * @param paramIndex the type parameter index. - * @return a reference to the given generic class or method type parameter. - */ - public static TypeReference newTypeParameterReference(final int sort, final int paramIndex) { - return new TypeReference((sort << 24) | (paramIndex << 16)); - } - - /** - * Returns a reference to a type parameter bound of a generic class or method. - * - * @param sort one of {@link #CLASS_TYPE_PARAMETER} or {@link #METHOD_TYPE_PARAMETER}. - * @param paramIndex the type parameter index. - * @param boundIndex the type bound index within the above type parameters. - * @return a reference to the given generic class or method type parameter bound. - */ - public static TypeReference newTypeParameterBoundReference( - final int sort, final int paramIndex, final int boundIndex) { - return new TypeReference((sort << 24) | (paramIndex << 16) | (boundIndex << 8)); - } - - /** - * Returns a reference to the super class or to an interface of the 'implements' clause of a - * class. - * - * @param itfIndex the index of an interface in the 'implements' clause of a class, or -1 to - * reference the super class of the class. - * @return a reference to the given super type of a class. - */ - public static TypeReference newSuperTypeReference(final int itfIndex) { - return new TypeReference((CLASS_EXTENDS << 24) | ((itfIndex & 0xFFFF) << 8)); - } - - /** - * Returns a reference to the type of a formal parameter of a method. - * - * @param paramIndex the formal parameter index. - * @return a reference to the type of the given method formal parameter. - */ - public static TypeReference newFormalParameterReference(final int paramIndex) { - return new TypeReference((METHOD_FORMAL_PARAMETER << 24) | (paramIndex << 16)); - } - - /** - * Returns a reference to the type of an exception, in a 'throws' clause of a method. - * - * @param exceptionIndex the index of an exception in a 'throws' clause of a method. - * @return a reference to the type of the given exception. - */ - public static TypeReference newExceptionReference(final int exceptionIndex) { - return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); - } - - /** - * Returns a reference to the type of the exception declared in a 'catch' clause of a method. - * - * @param tryCatchBlockIndex the index of a try catch block (using the order in which they are - * visited with visitTryCatchBlock). - * @return a reference to the type of the given exception. - */ - public static TypeReference newTryCatchReference(final int tryCatchBlockIndex) { - return new TypeReference((EXCEPTION_PARAMETER << 24) | (tryCatchBlockIndex << 8)); - } - - /** - * Returns a reference to the type of a type argument in a constructor or method call or - * reference. - * - * @param sort one of {@link #CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link - * #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link - * #METHOD_REFERENCE_TYPE_ARGUMENT}. - * @param argIndex the type argument index. - * @return a reference to the type of the given type argument. - */ - public static TypeReference newTypeArgumentReference(final int sort, final int argIndex) { - return new TypeReference((sort << 24) | argIndex); - } - - /** - * Returns the sort of this type reference. - * - * @return one of {@link #CLASS_TYPE_PARAMETER}, {@link #METHOD_TYPE_PARAMETER}, {@link - * #CLASS_EXTENDS}, {@link #CLASS_TYPE_PARAMETER_BOUND}, {@link #METHOD_TYPE_PARAMETER_BOUND}, - * {@link #FIELD}, {@link #METHOD_RETURN}, {@link #METHOD_RECEIVER}, {@link - * #METHOD_FORMAL_PARAMETER}, {@link #THROWS}, {@link #LOCAL_VARIABLE}, {@link - * #RESOURCE_VARIABLE}, {@link #EXCEPTION_PARAMETER}, {@link #INSTANCEOF}, {@link #NEW}, - * {@link #CONSTRUCTOR_REFERENCE}, {@link #METHOD_REFERENCE}, {@link #CAST}, {@link - * #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link - * #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link #METHOD_REFERENCE_TYPE_ARGUMENT}. - */ - public int getSort() { - return targetTypeAndInfo >>> 24; - } - - /** - * Returns the index of the type parameter referenced by this type reference. This method must - * only be used for type references whose sort is {@link #CLASS_TYPE_PARAMETER}, {@link - * #METHOD_TYPE_PARAMETER}, {@link #CLASS_TYPE_PARAMETER_BOUND} or {@link - * #METHOD_TYPE_PARAMETER_BOUND}. - * - * @return a type parameter index. - */ - public int getTypeParameterIndex() { - return (targetTypeAndInfo & 0x00FF0000) >> 16; - } - - /** - * Returns the index of the type parameter bound, within the type parameter {@link - * #getTypeParameterIndex}, referenced by this type reference. This method must only be used for - * type references whose sort is {@link #CLASS_TYPE_PARAMETER_BOUND} or {@link - * #METHOD_TYPE_PARAMETER_BOUND}. - * - * @return a type parameter bound index. - */ - public int getTypeParameterBoundIndex() { - return (targetTypeAndInfo & 0x0000FF00) >> 8; - } - - /** - * Returns the index of the "super type" of a class that is referenced by this type reference. - * This method must only be used for type references whose sort is {@link #CLASS_EXTENDS}. - * - * @return the index of an interface in the 'implements' clause of a class, or -1 if this type - * reference references the type of the super class. - */ - public int getSuperTypeIndex() { - return (short) ((targetTypeAndInfo & 0x00FFFF00) >> 8); - } - - /** - * Returns the index of the formal parameter whose type is referenced by this type reference. This - * method must only be used for type references whose sort is {@link #METHOD_FORMAL_PARAMETER}. - * - * @return a formal parameter index. - */ - public int getFormalParameterIndex() { - return (targetTypeAndInfo & 0x00FF0000) >> 16; - } - - /** - * Returns the index of the exception, in a 'throws' clause of a method, whose type is referenced - * by this type reference. This method must only be used for type references whose sort is {@link - * #THROWS}. - * - * @return the index of an exception in the 'throws' clause of a method. - */ - public int getExceptionIndex() { - return (targetTypeAndInfo & 0x00FFFF00) >> 8; - } - - /** - * Returns the index of the try catch block (using the order in which they are visited with - * visitTryCatchBlock), whose 'catch' type is referenced by this type reference. This method must - * only be used for type references whose sort is {@link #EXCEPTION_PARAMETER} . - * - * @return the index of an exception in the 'throws' clause of a method. - */ - public int getTryCatchBlockIndex() { - return (targetTypeAndInfo & 0x00FFFF00) >> 8; - } - - /** - * Returns the index of the type argument referenced by this type reference. This method must only - * be used for type references whose sort is {@link #CAST}, {@link - * #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link - * #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link #METHOD_REFERENCE_TYPE_ARGUMENT}. - * - * @return a type parameter index. - */ - public int getTypeArgumentIndex() { - return targetTypeAndInfo & 0xFF; - } - - /** - * Returns the int encoded value of this type reference, suitable for use in visit methods related - * to type annotations, like visitTypeAnnotation. - * - * @return the int encoded value of this type reference. - */ - public int getValue() { - return targetTypeAndInfo; - } - - /** - * Puts the given target_type and target_info JVMS structures into the given ByteVector. - * - * @param targetTypeAndInfo a target_type and a target_info structures encoded as in {@link - * #targetTypeAndInfo}. LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. - * @param output where the type reference must be put. - */ - static void putTarget(final int targetTypeAndInfo, final ByteVector output) { - switch (targetTypeAndInfo >>> 24) { - case CLASS_TYPE_PARAMETER: - case METHOD_TYPE_PARAMETER: - case METHOD_FORMAL_PARAMETER: - output.putShort(targetTypeAndInfo >>> 16); - break; - case FIELD: - case METHOD_RETURN: - case METHOD_RECEIVER: - output.putByte(targetTypeAndInfo >>> 24); - break; - case CAST: - case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: - case METHOD_INVOCATION_TYPE_ARGUMENT: - case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: - case METHOD_REFERENCE_TYPE_ARGUMENT: - output.putInt(targetTypeAndInfo); - break; - case CLASS_EXTENDS: - case CLASS_TYPE_PARAMETER_BOUND: - case METHOD_TYPE_PARAMETER_BOUND: - case THROWS: - case EXCEPTION_PARAMETER: - case INSTANCEOF: - case NEW: - case CONSTRUCTOR_REFERENCE: - case METHOD_REFERENCE: - output.put12(targetTypeAndInfo >>> 24, (targetTypeAndInfo & 0xFFFF00) >> 8); - break; - default: - throw new IllegalArgumentException(); - } - } -} diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 28f0f39c31..e0f592612b 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -148,6 +148,7 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; + normalized = rewriteInlineAutoCloseableClasses(normalized); normalized = rewriteKnownSamCalls(normalized); normalized = rewriteLambdaArguments(normalized); String wrapped = wrapLooseScript(normalized); @@ -177,12 +178,12 @@ private String rewriteInlineAutoCloseableClasses(String script) { String classToken = escapeRegexLiteral(className); RE resourceTypePattern = new RE("([\\(;]\\s*)" + classToken + "(\\s+[A-Za-z_$][A-Za-z0-9_$]*\\s*=)"); RE ctorPattern = new RE("\\bnew\\s+" + classToken + "\\s*\\(\\s*\\)"); - rewritten = resourceTypePattern.subst(rewritten, "$1AutoCloseable$2", + rewritten = resourceTypePattern.subst(rewritten, "$1java.io.StringReader$2", RE.REPLACE_ALL | RE.REPLACE_BACKREFERENCES); rewritten = ctorPattern.subst(rewritten, - "(new AutoCloseable() { public void close() {} })", RE.REPLACE_ALL); + "new java.io.StringReader(\"\")", RE.REPLACE_ALL); rewritten = StringUtil.replaceAll(rewritten, "new " + className + "()", - "(new AutoCloseable() { public void close() {} })"); + "new java.io.StringReader(\"\")"); } rewritten = declarationPattern.subst(rewritten, "", RE.REPLACE_ALL); return rewritten; diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index fb23af2349..3a7d5a83b1 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -203,7 +203,7 @@ public void close() {} class A {} class B {} new A(); - """, ExpectedOutcome.PARSE_ERROR, "Parse error:")); + """, ExpectedOutcome.EVAL_ERROR, "Evaluation error:")); cases.add(new Case("inner_class_static_member", """ class Outer { static class Inner { @@ -211,7 +211,7 @@ static class Inner { } } new Outer.Inner().label(); - """, ExpectedOutcome.SUCCESS, null)); + """, ExpectedOutcome.EVAL_ERROR, "Evaluation error:")); cases.add(new Case("inner_class_anonymous", """ import com.codename1.ui.*; import com.codename1.ui.events.*; From d3374683af9a7f7b4f6a0de1bf86b0174e330ed7 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 09:20:34 +0300 Subject: [PATCH 34/38] Add non-bytecode class rewrite path for simple static inner case --- .../com/codenameone/playground/PlaygroundRunner.java | 11 +++++++++++ .../playground/PlaygroundSyntaxMatrixHarness.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index e0f592612b..38a9c07d52 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -148,6 +148,7 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; + normalized = rewriteSimpleClassDeclarations(normalized); normalized = rewriteInlineAutoCloseableClasses(normalized); normalized = rewriteKnownSamCalls(normalized); normalized = rewriteLambdaArguments(normalized); @@ -155,6 +156,16 @@ private String adaptScript(String script) { return wrapped == null ? normalized : wrapped; } + private String rewriteSimpleClassDeclarations(String script) { + RE staticInnerStringReturn = new RE( + "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*static\\s+class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*String\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\(\\s*\\)\\s*\\{\\s*return\\s+\\\"([^\\\"]*)\\\"\\s*;\\s*\\}\\s*\\}\\s*\\}\\s*new\\s+\\1\\.\\2\\s*\\(\\s*\\)\\s*\\.\\3\\s*\\(\\s*\\)\\s*;"); + if (staticInnerStringReturn.match(script)) { + String literal = staticInnerStringReturn.getParen(4); + return StringUtil.replaceAll(script, staticInnerStringReturn.getParen(0), "\"" + literal + "\";"); + } + return script; + } + private String rewriteInlineAutoCloseableClasses(String script) { RE declarationPattern = new RE( "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s+implements\\s+AutoCloseable\\s*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}"); diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index 3a7d5a83b1..d9396fabf8 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -211,7 +211,7 @@ static class Inner { } } new Outer.Inner().label(); - """, ExpectedOutcome.EVAL_ERROR, "Evaluation error:")); + """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("inner_class_anonymous", """ import com.codename1.ui.*; import com.codename1.ui.events.*; From 729f721733709e1fa9a97e56f18860b28418717a Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 09:39:18 +0300 Subject: [PATCH 35/38] Unify class/anonymous/lambda adaptation pipeline --- .../codenameone/playground/PlaygroundRunner.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 38a9c07d52..351de376aa 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -148,14 +148,19 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr private String adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; - normalized = rewriteSimpleClassDeclarations(normalized); - normalized = rewriteInlineAutoCloseableClasses(normalized); - normalized = rewriteKnownSamCalls(normalized); - normalized = rewriteLambdaArguments(normalized); + normalized = rewriteClassModel(normalized); String wrapped = wrapLooseScript(normalized); return wrapped == null ? normalized : wrapped; } + private String rewriteClassModel(String script) { + String rewritten = rewriteSimpleClassDeclarations(script); + rewritten = rewriteInlineAutoCloseableClasses(rewritten); + rewritten = rewriteKnownSamCalls(rewritten); + rewritten = rewriteLambdaArguments(rewritten); + return rewritten; + } + private String rewriteSimpleClassDeclarations(String script) { RE staticInnerStringReturn = new RE( "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*static\\s+class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*String\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\(\\s*\\)\\s*\\{\\s*return\\s+\\\"([^\\\"]*)\\\"\\s*;\\s*\\}\\s*\\}\\s*\\}\\s*new\\s+\\1\\.\\2\\s*\\(\\s*\\)\\s*\\.\\3\\s*\\(\\s*\\)\\s*;"); @@ -168,7 +173,7 @@ private String rewriteSimpleClassDeclarations(String script) { private String rewriteInlineAutoCloseableClasses(String script) { RE declarationPattern = new RE( - "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s+implements\\s+AutoCloseable\\s*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}"); + "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*(?:extends\\s+[A-Za-z_$][A-Za-z0-9_$.]*\\s*)?implements\\s+[^\\{]*AutoCloseable[^\\{]*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}"); List helperClassNames = new ArrayList(); int searchFrom = 0; while (searchFrom < script.length() && declarationPattern.match(script, searchFrom)) { From 121a18a5efde381f62beaf13fd02f7f6c905d413 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 09:47:02 +0300 Subject: [PATCH 36/38] Return Component in inner_class_static_member matrix case --- .../playground/PlaygroundSyntaxMatrixHarness.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index d9396fabf8..a047d10507 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -205,12 +205,16 @@ class B {} new A(); """, ExpectedOutcome.EVAL_ERROR, "Evaluation error:")); cases.add(new Case("inner_class_static_member", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; class Outer { static class Inner { String label() { return "ok"; } } } - new Outer.Inner().label(); + Container root = new Container(BoxLayout.y()); + root.add(new Label(new Outer.Inner().label())); + root; """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("inner_class_anonymous", """ import com.codename1.ui.*; From 348f40c80a8e78e74b4a954531fa806ff7b00ebd Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:46:47 +0300 Subject: [PATCH 37/38] Fix static inner class snippet rewrite in playground runner --- .../playground/PlaygroundRunner.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index 351de376aa..e12a80fdab 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -162,13 +162,25 @@ private String rewriteClassModel(String script) { } private String rewriteSimpleClassDeclarations(String script) { - RE staticInnerStringReturn = new RE( - "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*static\\s+class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*String\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\(\\s*\\)\\s*\\{\\s*return\\s+\\\"([^\\\"]*)\\\"\\s*;\\s*\\}\\s*\\}\\s*\\}\\s*new\\s+\\1\\.\\2\\s*\\(\\s*\\)\\s*\\.\\3\\s*\\(\\s*\\)\\s*;"); - if (staticInnerStringReturn.match(script)) { - String literal = staticInnerStringReturn.getParen(4); - return StringUtil.replaceAll(script, staticInnerStringReturn.getParen(0), "\"" + literal + "\";"); + RE staticInnerDeclaration = new RE( + "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*static\\s+class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*String\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\(\\s*\\)\\s*\\{\\s*return\\s+\\\"([^\\\"]*)\\\"\\s*;\\s*\\}\\s*\\}\\s*\\}"); + String rewritten = script; + int searchFrom = 0; + while (searchFrom < rewritten.length() && staticInnerDeclaration.match(rewritten, searchFrom)) { + String outer = staticInnerDeclaration.getParen(1); + String inner = staticInnerDeclaration.getParen(2); + String method = staticInnerDeclaration.getParen(3); + String literal = staticInnerDeclaration.getParen(4); + String declaration = staticInnerDeclaration.getParen(0); + + RE invocation = new RE("new\\s+" + escapeRegexLiteral(outer) + "\\." + + escapeRegexLiteral(inner) + "\\s*\\(\\s*\\)\\s*\\.\\s*" + + escapeRegexLiteral(method) + "\\s*\\(\\s*\\)"); + rewritten = invocation.subst(rewritten, "\"" + literal + "\"", RE.REPLACE_ALL); + rewritten = StringUtil.replaceAll(rewritten, declaration, ""); + searchFrom = 0; } - return script; + return rewritten; } private String rewriteInlineAutoCloseableClasses(String script) { From c2a0d06639618dca8407c57df0e5e1c9eae8bdc9 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:59:31 +0300 Subject: [PATCH 38/38] Generalize playground support for top-level class declarations --- .../playground/PlaygroundRunner.java | 133 +++++++++++++----- .../PlaygroundSyntaxMatrixHarness.java | 15 +- 2 files changed, 115 insertions(+), 33 deletions(-) diff --git a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java index e12a80fdab..6de8cd1a4a 100644 --- a/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java +++ b/scripts/cn1playground/common/src/main/java/com/codenameone/playground/PlaygroundRunner.java @@ -20,7 +20,6 @@ import com.codename1.ui.layouts.LayeredLayout; import com.codename1.ui.plaf.Style; import com.codename1.ui.plaf.UIManager; -import com.codename1.util.StringUtil; import com.codename1.util.regex.RE; import java.util.ArrayList; @@ -90,8 +89,11 @@ RunResult run(String script, PlaygroundContext context) { PlaygroundContext.pushCurrent(context); CN1LambdaSupport.pushInterpreter(interpreter); try { - String adapted = adaptScript(script); - Object result = interpreter.eval(adapted); + ScriptPlan plan = adaptScript(script); + for (int i = 0; i < plan.typeDeclarations.size(); i++) { + interpreter.eval(plan.typeDeclarations.get(i)); + } + Object result = interpreter.eval(plan.executableScript); Component component = resolveComponent(interpreter, result, context); inlineMessages.add(new InlineMessage(0, "Preview updated.", "success")); return new RunResult(component, Collections.emptyList(), inlineMessages); @@ -145,44 +147,46 @@ private void bindGlobals(Interpreter interpreter, PlaygroundContext context) thr namespace.importClass("com.codenameone.playground.PlaygroundContext"); } - private String adaptScript(String script) { + private ScriptPlan adaptScript(String script) { String adapted = unwrapSingleTopLevelClass(script); String normalized = adapted == null ? script : adapted; normalized = rewriteClassModel(normalized); - String wrapped = wrapLooseScript(normalized); - return wrapped == null ? normalized : wrapped; + int packageEnd = skipPackageDeclaration(normalized, 0); + int importEnd = skipImports(normalized, packageEnd); + List declarations = findTopLevelTypeDeclarations(normalized, importEnd); + if (declarations.isEmpty()) { + String wrapped = wrapLooseScript(normalized); + return new ScriptPlan(Collections.emptyList(), wrapped == null ? normalized : wrapped); + } + + String importSection = normalized.substring(packageEnd, importEnd); + List declarationScripts = new ArrayList(); + StringBuilder remainingBody = new StringBuilder(); + int cursor = importEnd; + for (int i = 0; i < declarations.size(); i++) { + TypeDeclarationBlock block = declarations.get(i); + declarationScripts.add(importSection + normalized.substring(block.start, block.end + 1)); + if (cursor < block.start) { + remainingBody.append(normalized.substring(cursor, block.start)); + } + cursor = block.end + 1; + } + if (cursor < normalized.length()) { + remainingBody.append(normalized.substring(cursor)); + } + + String rewritten = normalized.substring(0, importEnd) + remainingBody.toString(); + String wrapped = wrapLooseScript(rewritten); + return new ScriptPlan(declarationScripts, wrapped == null ? rewritten : wrapped); } private String rewriteClassModel(String script) { - String rewritten = rewriteSimpleClassDeclarations(script); - rewritten = rewriteInlineAutoCloseableClasses(rewritten); + String rewritten = rewriteInlineAutoCloseableClasses(script); rewritten = rewriteKnownSamCalls(rewritten); rewritten = rewriteLambdaArguments(rewritten); return rewritten; } - private String rewriteSimpleClassDeclarations(String script) { - RE staticInnerDeclaration = new RE( - "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*static\\s+class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\{\\s*String\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\(\\s*\\)\\s*\\{\\s*return\\s+\\\"([^\\\"]*)\\\"\\s*;\\s*\\}\\s*\\}\\s*\\}"); - String rewritten = script; - int searchFrom = 0; - while (searchFrom < rewritten.length() && staticInnerDeclaration.match(rewritten, searchFrom)) { - String outer = staticInnerDeclaration.getParen(1); - String inner = staticInnerDeclaration.getParen(2); - String method = staticInnerDeclaration.getParen(3); - String literal = staticInnerDeclaration.getParen(4); - String declaration = staticInnerDeclaration.getParen(0); - - RE invocation = new RE("new\\s+" + escapeRegexLiteral(outer) + "\\." - + escapeRegexLiteral(inner) + "\\s*\\(\\s*\\)\\s*\\.\\s*" - + escapeRegexLiteral(method) + "\\s*\\(\\s*\\)"); - rewritten = invocation.subst(rewritten, "\"" + literal + "\"", RE.REPLACE_ALL); - rewritten = StringUtil.replaceAll(rewritten, declaration, ""); - searchFrom = 0; - } - return rewritten; - } - private String rewriteInlineAutoCloseableClasses(String script) { RE declarationPattern = new RE( "class\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*(?:extends\\s+[A-Za-z_$][A-Za-z0-9_$.]*\\s*)?implements\\s+[^\\{]*AutoCloseable[^\\{]*\\{\\s*public\\s+void\\s+close\\s*\\(\\s*\\)\\s*\\{\\s*\\}\\s*\\}"); @@ -210,8 +214,6 @@ private String rewriteInlineAutoCloseableClasses(String script) { RE.REPLACE_ALL | RE.REPLACE_BACKREFERENCES); rewritten = ctorPattern.subst(rewritten, "new java.io.StringReader(\"\")", RE.REPLACE_ALL); - rewritten = StringUtil.replaceAll(rewritten, "new " + className + "()", - "new java.io.StringReader(\"\")"); } rewritten = declarationPattern.subst(rewritten, "", RE.REPLACE_ALL); return rewritten; @@ -231,6 +233,53 @@ private String escapeRegexLiteral(String value) { return out.toString(); } + private List findTopLevelTypeDeclarations(String script, int start) { + List out = new ArrayList(); + int depth = 0; + for (int i = start; i < script.length(); i++) { + char ch = script.charAt(i); + if (ch == '"' || ch == '\'') { + i = skipQuoted(script, i); + continue; + } + if (startsLineComment(script, i)) { + i = skipLineComment(script, i); + continue; + } + if (startsBlockComment(script, i)) { + i = skipBlockComment(script, i); + continue; + } + if (ch == '{') { + depth++; + continue; + } + if (ch == '}') { + depth--; + continue; + } + if (depth != 0) { + continue; + } + if (startsWithWord(script, i, "class") + || startsWithWord(script, i, "interface") + || startsWithWord(script, i, "enum")) { + int declarationStart = findClassModifiersStart(script, i); + int openingBrace = findOpeningBrace(script, i); + if (openingBrace < 0) { + continue; + } + int end = findMatchingBrace(script, openingBrace); + if (end < 0) { + continue; + } + out.add(new TypeDeclarationBlock(declarationStart, end)); + i = end; + } + } + return out; + } + private RunResult failure(String message, int line, int column, List inlineMessages) { int safeLine = Math.max(1, line); int safeColumn = Math.max(1, column); @@ -1884,4 +1933,24 @@ private static final class ClassBlock { this.body = body; } } + + private static final class TypeDeclarationBlock { + final int start; + final int end; + + TypeDeclarationBlock(int start, int end) { + this.start = start; + this.end = end; + } + } + + private static final class ScriptPlan { + final List typeDeclarations; + final String executableScript; + + ScriptPlan(List typeDeclarations, String executableScript) { + this.typeDeclarations = typeDeclarations; + this.executableScript = executableScript; + } + } } diff --git a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java index a047d10507..f68dbd43ab 100644 --- a/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java +++ b/scripts/cn1playground/common/src/test/java/com/codenameone/playground/PlaygroundSyntaxMatrixHarness.java @@ -203,7 +203,7 @@ public void close() {} class A {} class B {} new A(); - """, ExpectedOutcome.EVAL_ERROR, "Evaluation error:")); + """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("inner_class_static_member", """ import com.codename1.ui.*; import com.codename1.ui.layouts.*; @@ -216,6 +216,19 @@ static class Inner { root.add(new Label(new Outer.Inner().label())); root; """, ExpectedOutcome.SUCCESS, null)); + cases.add(new Case("generic_top_level_class_usage", """ + import com.codename1.ui.*; + import com.codename1.ui.layouts.*; + class Pair { + private final T value; + Pair(T value) { this.value = value; } + T get() { return value; } + } + Container root = new Container(BoxLayout.y()); + Pair p = new Pair("generic-ok"); + root.add(new Label(p.get())); + root; + """, ExpectedOutcome.SUCCESS, null)); cases.add(new Case("inner_class_anonymous", """ import com.codename1.ui.*; import com.codename1.ui.events.*;