Skip to content

Commit

Permalink
Merge branch 'next'
Browse files Browse the repository at this point in the history
  • Loading branch information
softwareCobbler committed Aug 26, 2024
2 parents cffec8e + 2dd7883 commit 6fda10e
Show file tree
Hide file tree
Showing 21 changed files with 490 additions and 208 deletions.
23 changes: 7 additions & 16 deletions luceedebug/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java application project to get you started.
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
* User Manual available at https://docs.gradle.org/7.3/userguide/building_java_projects.html
*/

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import java.nio.file.Paths;

import xbuild.XBuild.GenerateConstants

plugins {
java
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.gradleup.shadow") version "8.3.0"
id("org.owasp.dependencycheck") version "8.4.0" apply false
id("buildPluginX")
}
Expand All @@ -32,10 +21,11 @@ repositories {
}

dependencies {
// Use JUnit Jupiter for testing.
testImplementation("org.junit.jupiter:junit-jupiter:5.7.2")
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params
testImplementation("org.junit.jupiter:junit-jupiter-params:5.11.0")
// https://mvnrepository.com/artifact/com.github.docker-java/docker-java-core
testImplementation("com.github.docker-java:docker-java-core:3.3.0")
// https://mvnrepository.com/artifact/com.github.docker-java/docker-java-transport-httpclient5
Expand Down Expand Up @@ -106,9 +96,10 @@ tasks.jar {
}
}

val luceedebugVersion = "2.0.12"
val luceedebugVersion = "2.0.13.2"
val libfile = "luceedebug-" + luceedebugVersion + ".jar"

// TODO: this should, but does not currently, participate in the `clean` task, so the generated file sticks around after invoking `clean`.
tasks.register<GenerateConstants>("generateJavaConstantsFile") {
version = luceedebugVersion
// n.b. this ends up in src/luceedebug/generated, rather than build/...
Expand Down
81 changes: 74 additions & 7 deletions luceedebug/src/main/java/luceedebug/Agent.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package luceedebug;

import java.lang.instrument.*;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarFile;
import java.io.File;

Expand Down Expand Up @@ -132,6 +134,70 @@ static class AgentArgs {
}
}

/**
* There is probably a way to do this automatically, but we do it manually for now.
* The injected classes need to be injected in a particular order with respect to class hierarchy.
* For classes that don't derive from anything, order is irrelevant; but if, within the coreinject package,
* there are hierarchies, the supertypes need to be loaded first, then the subtype, then the subsubtype, and so on.
* If a class is added to coreinject and we do not have ordering information for it here, the agent should fail to start
* with a message regarding which class was missing this information.
*/
private static class CoreInjectionLinearization {
private static Map<String, Integer> linearizedCoreInjectClasses() {
var result = new HashMap<String, Integer>();

result.put("luceedebug.coreinject.LuceeVm$BpLineAndId", 0);
result.put("luceedebug.coreinject.DebugManager$2", 0);
result.put("luceedebug.coreinject.DebugManager$CfStepRequest", 0);
result.put("luceedebug.coreinject.LuceeVm$ReplayableCfBreakpointRequest", 0);
result.put("luceedebug.coreinject.Utils", 0);
result.put("luceedebug.coreinject.ValTracker$WeakTaggedObject", 0);
result.put("luceedebug.coreinject.DebugManager$1", 0);
result.put("luceedebug.coreinject.ClosureScopeLocalScopeAccessorShim", 0);
result.put("luceedebug.coreinject.ComponentScopeMarkerTraitShim", 0);
result.put("luceedebug.coreinject.DebugFrame$FrameContext", 0);
result.put("luceedebug.coreinject.LuceeVm$SteppingState", 0);
result.put("luceedebug.coreinject.LuceeVm$KlassMap", 0);
result.put("luceedebug.coreinject.LuceeVm$JdwpWorker", 0);
result.put("luceedebug.coreinject.ValTracker$TaggedObject", 0);
result.put("luceedebug.coreinject.DebugEntity", 0);
result.put("luceedebug.coreinject.Breakpoint", 0);
result.put("luceedebug.coreinject.CfValueDebuggerBridge$MarkerTrait", 0);
result.put("luceedebug.coreinject.ValTracker", 0);
result.put("luceedebug.coreinject.UnsafeUtils", 0);
result.put("luceedebug.coreinject.DebugFrame", 0);
result.put("luceedebug.coreinject.CfValueDebuggerBridge$MarkerTrait$Scope", 0);
result.put("luceedebug.coreinject.DebugManager$PageContextAndOutputStream", 0);
result.put("luceedebug.coreinject.LuceeVm$ThreadMap", 0);
result.put("luceedebug.coreinject.DebugManager", 0);
result.put("luceedebug.coreinject.LuceeVm$JdwpStaticCallable", 0);
result.put("luceedebug.coreinject.CfValueDebuggerBridge", 0);
result.put("luceedebug.coreinject.DebugFrame$FrameContext$SupplierOrNull", 0);
result.put("luceedebug.coreinject.LuceeVm", 0);
result.put("luceedebug.coreinject.ValTracker$CleanerRunner", 0);
result.put("luceedebug.coreinject.ExprEvaluator", 0);

result.put("luceedebug.coreinject.ExprEvaluator$Evaluator", 0);
result.put("luceedebug.coreinject.ExprEvaluator$Lucee6Evaluator", 1);
result.put("luceedebug.coreinject.ExprEvaluator$Lucee5Evaluator", 1);

return result;
}

public static Comparator<ClassInjection> comparator() {
final Map<String, Integer> ordering = linearizedCoreInjectClasses();
return Comparator.comparing(injection -> {
var v = ordering.get(injection.name);
if (v == null) {
throw new RuntimeException("Missing linearized sort order information for class '" + injection.name + "'");
}
else {
return v;
}
});
}
}

public static void premain(String argString, Instrumentation inst) throws Throwable {
final var parsedArgs = new AgentArgs(argString);

Expand All @@ -141,7 +207,13 @@ public static void premain(String argString, Instrumentation inst) throws Throwa
System.exit(1);
}

/**
* Generally useful for debugging, and also has the side-effect of causing the engine
* to write the full absolute file paths into the generated classfiles, which we need
* to match up classfiles to IDE sourcefiles.
*/
System.setProperty("lucee.requesttimeout", "false");

//
// This used to be accomplished by instrumenting the Felix constructor (that we expect Lucee to be using).
// Lucee later added the ability to specify these as envvars or system properties, see:
Expand Down Expand Up @@ -180,12 +252,7 @@ public static void premain(String argString, Instrumentation inst) throws Throwa
return null;
}
})
.sorted((l,r) -> {
// we don't need to sort, if we have no dependencies from within coreinject into coreinject
// (i.e. there is class/interface in coreinject.* that extends/implements another class/interface in coreinject.*)
// otherwise, we'd have to inject parents first, then children, then grandchildren, etc.
return 0;
})
.sorted(CoreInjectionLinearization.comparator())
.toArray(size -> new ClassInjection[size]);

final var config = new Config(Config.checkIfFileSystemIsCaseSensitive(parsedArgs.jarPath));
Expand Down
54 changes: 23 additions & 31 deletions luceedebug/src/main/java/luceedebug/Config.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
package luceedebug;

import java.io.File;

public class Config {
private final boolean fsIsCaseSensitive_;
// we probably never want to step into this (the a=b in `function foo(a=b) { ... }` )
// but for now it's configurable
private boolean stepIntoUdfDefaultValueInitFrames_ = false;

Config(boolean fsIsCaseSensitive) {
this.fsIsCaseSensitive_ = fsIsCaseSensitive;
}

public boolean getStepIntoUdfDefaultValueInitFrames() {
return this.stepIntoUdfDefaultValueInitFrames_;
}
public void setStepIntoUdfDefaultValueInitFrames(boolean v) {
this.stepIntoUdfDefaultValueInitFrames_ = v;
}

private static String invertCase(String path) {
int offset = 0;
int strLen = path.length();

public class Config {
private final boolean fsIsCaseSensitive_;
// we probably never want to step into this (the a=b in `function foo(a=b) { ... }` )
// but for now it's configurable
private boolean stepIntoUdfDefaultValueInitFrames_ = false;

Config(boolean fsIsCaseSensitive) {
this.fsIsCaseSensitive_ = fsIsCaseSensitive;
}

public boolean getStepIntoUdfDefaultValueInitFrames() {
return this.stepIntoUdfDefaultValueInitFrames_;
}
public void setStepIntoUdfDefaultValueInitFrames(boolean v) {
this.stepIntoUdfDefaultValueInitFrames_ = v;
}

private static String invertCase(String path) {
int offset = 0;
int strLen = path.length();
final var builder = new StringBuilder();
while (offset < strLen) {
int c = path.codePointAt(offset);
Expand Down Expand Up @@ -50,16 +50,8 @@ public boolean getFsIsCaseSensitive() {
return fsIsCaseSensitive_;
}

public String canonicalizePath(String path) {
if (fsIsCaseSensitive_) {
return path;
}
else {
return path.toLowerCase();
}
public static String canonicalizeFileName(String s) {
return s.replaceAll("[\\\\/]+", "/").toLowerCase();
}

public OriginalAndTransformedString canonicalizedPath(String path) {
return new OriginalAndTransformedString(path, canonicalizePath(path));
}
}
54 changes: 24 additions & 30 deletions luceedebug/src/main/java/luceedebug/DapServer.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package luceedebug;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
Expand All @@ -9,11 +8,9 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.lsp4j.debug.*;
Expand All @@ -34,42 +31,36 @@ public class DapServer implements IDebugProtocolServer {
private final Config config_;
private ArrayList<IPathTransform> pathTransforms = new ArrayList<>();

interface TransformRunner {
Optional<String> run(IPathTransform transform, String s);
}

// for dev, system.out was fine, in some containers, others totally suppress it and it doesn't even
// end up in log files.
// this is all jacked up, on runwar builds it spits out two lines per call to `logger.info(...)` message, the first one being [ERROR] which is not right
private static final Logger logger = Logger.getLogger("luceedebug");

/**
* runs all the transforms until one matches and produces a result
* if no transform matches, returns the input string unmodified
*/
private String applyPathTransforms(String s, TransformRunner runner) {
private String applyPathTransformsIdeToCf(String s) {
for (var transform : pathTransforms) {
var result = runner.run(transform, s);
var result = transform.ideToServer(s);
if (result.isPresent()) {
return config_.canonicalizePath(result.get());
return Config.canonicalizeFileName(result.get());
}
}
// no transform matched, but still needs canonicalization
return config_.canonicalizePath(s);
}

private String applyPathTransformsIdeToCf(String s) {
return applyPathTransforms(
s,
(transform, path) -> transform.ideToServer(path)
).replaceAll("\\\\|/", Matcher.quoteReplacement(File.separator));
// no transform matched, but still needs canonicalization
return Config.canonicalizeFileName(s);
}

private String applyPathTransformsCfToIde(String s) {
return applyPathTransforms(
s,
(transform, path) -> transform.serverToIde(path)
);
/**
* n.b. do _not_ canonicalize when sending back to IDE
*/
private String applyPathTransformsServerToIde(String s) {
for (var transform : pathTransforms) {
var result = transform.serverToIde(s);
if (result.isPresent()) {
return result.get();
}
}

// no match
return s;
}

private IDebugProtocolClient clientProxy_;
Expand Down Expand Up @@ -323,7 +314,7 @@ public CompletableFuture<StackTraceResponse> stackTrace(StackTraceArguments args

for (var cfFrame : luceeVm_.getStackTrace(args.getThreadId())) {
final var source = new Source();
source.setPath(applyPathTransformsCfToIde(cfFrame.getSourceFilePath()));
source.setPath(applyPathTransformsServerToIde(cfFrame.getSourceFilePath()));

final var lspFrame = new org.eclipse.lsp4j.debug.StackFrame();
lspFrame.setId((int)cfFrame.getId());
Expand Down Expand Up @@ -385,7 +376,10 @@ public CompletableFuture<VariablesResponse> variables(VariablesArguments args) {

@Override
public CompletableFuture<SetBreakpointsResponse> setBreakpoints(SetBreakpointsArguments args) {
final var path = new OriginalAndTransformedString(args.getSource().getPath(), applyPathTransformsIdeToCf(args.getSource().getPath()));
final var path = new OriginalAndTransformedString(
args.getSource().getPath(),
applyPathTransformsIdeToCf(args.getSource().getPath())
);
logger.finest("bp for " + path.original + " -> " + path.transformed);
final int size = args.getBreakpoints().length;
final int[] lines = new int[size];
Expand Down Expand Up @@ -744,7 +738,7 @@ CompletableFuture<GetSourcePathResponse> getSourcePath(GetSourcePathArguments ar
final var serverPath = luceeVm_.getSourcePathForVariablesRef(args.getVariablesReference());

if (serverPath != null) {
response.setPath(applyPathTransformsCfToIde(serverPath));
response.setPath(applyPathTransformsServerToIde(serverPath));
}
else {
response.setPath(null);
Expand Down
3 changes: 3 additions & 0 deletions luceedebug/src/main/java/luceedebug/IDebugManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public interface CfStepCallback {
// These must be the only "entry points" from lucee compiled CF files into luceedebug.
public void luceedebug_stepNotificationEntry_step(int currentLine);
public void luceedebug_stepNotificationEntry_stepAfterCompletedUdfCall();
static public boolean isStepNotificationEntryFunc(String methodName) {
return methodName.startsWith("luceedebug_stepNotificationEntry_");
}

public void registerStepRequest(Thread thread, int stepType);
public void clearStepRequest(Thread thread);
Expand Down
Loading

0 comments on commit 6fda10e

Please sign in to comment.