Skip to content

Commit

Permalink
Add support to debug Pure IDE executions (#3314)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelbey authored Dec 24, 2024
1 parent 3fe4a3d commit d2e84ca
Show file tree
Hide file tree
Showing 12 changed files with 586 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@
<artifactId>legend-engine-pure-ide-light-pure</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-ide-light-interpreted-functions</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.finos.legend.engine</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.finos.legend.engine.ide.api.concept.Concept;
import org.finos.legend.engine.ide.api.concept.MovePackageableElements;
import org.finos.legend.engine.ide.api.concept.RenameConcept;
import org.finos.legend.engine.ide.api.debug.Debugging;
import org.finos.legend.engine.ide.api.execution.function.Execute;
import org.finos.legend.engine.ide.api.execution.go.ExecuteGo;
import org.finos.legend.engine.ide.api.execution.test.ExecuteTests;
Expand Down Expand Up @@ -106,6 +107,7 @@ public void run(ServerConfiguration configuration, Environment environment) thro
environment.jersey().register(new Execute(pureSession));
environment.jersey().register(new ExecuteGo(pureSession));
environment.jersey().register(new ExecuteTests(pureSession));
environment.jersey().register(new Debugging(pureSession));

environment.jersey().register(new FindInSources(pureSession));
environment.jersey().register(new FindPureFile(pureSession));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2024 Goldman Sachs
//
// Licensed 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.
//

package org.finos.legend.engine.ide.api.debug;

import io.swagger.annotations.Api;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import org.eclipse.collections.impl.factory.Maps;
import org.finos.legend.engine.ide.session.PureSession;
import org.finos.legend.engine.pure.ide.interpreted.debug.DebugState;
import org.finos.legend.engine.pure.ide.interpreted.debug.FunctionExecutionInterpretedWithDebugSupport;
import org.finos.legend.pure.m3.execution.FunctionExecution;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

@Api(tags = "Debug")
@Path("/")
public class Debugging
{
private final PureSession pureSession;

public Debugging(PureSession pureSession)
{
this.pureSession = pureSession;
}

@POST
@Path("debugging")
public Response debugging(@Context HttpServletRequest request, @Context HttpServletResponse response) throws Exception
{
FunctionExecution functionExecution = pureSession.getFunctionExecution();
if (functionExecution instanceof FunctionExecutionInterpretedWithDebugSupport)
{
FunctionExecutionInterpretedWithDebugSupport debugSupport = (FunctionExecutionInterpretedWithDebugSupport) functionExecution;
DebugState debugState = debugSupport.getDebugState();
if (debugState == null)
{
return Response.ok(getText("Not on debug state!")).build();
}

JSONObject mainObject = (JSONObject) new JSONParser().parse(new InputStreamReader(request.getInputStream()));
JSONObject extraParams = (JSONObject) mainObject.get("extraParams");
List<String> args = (List<String>) extraParams.get("args");

if (args.isEmpty())
{
args.add("summary");
}

switch (args.get(0))
{
case "summary":
return debugSummary(debugState);
case "abort":
return debugAbort(debugState);
default: // no command is shortcut for evaluation
return debugEvaluate(debugState, String.join(" ", args));
}
}
else
{
return Response.status(Response.Status.BAD_REQUEST).entity("Environment does not support debug!").build();
}
}

private Response debugAbort(DebugState debugState)
{
debugState.abort();
return Response.ok(getText("aborting execution...")).build();
}

private Response debugSummary(DebugState debugState)
{
return Response.ok(getText(debugState.getSummary())).build();
}

private Response debugEvaluate(DebugState debugState, String command)
{
try
{
return Response.ok(getText(debugState.evaluate(command))).build();
}
catch (Exception e)
{
return Response.status(Response.Status.BAD_REQUEST).entity(getText(e.getMessage())).build();
}
}

private static Map<String, String> getText(String value)
{
return Maps.fixedSize.of("text", value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.finos.legend.engine.ide.SourceLocationConfiguration;
import org.finos.legend.engine.ide.api.execution.test.CallBack;
import org.finos.legend.engine.ide.helpers.response.IDEResponse;
import org.finos.legend.engine.pure.ide.interpreted.debug.FunctionExecutionInterpretedWithDebugSupport;
import org.finos.legend.pure.m3.SourceMutation;
import org.finos.legend.pure.m3.execution.FunctionExecution;
import org.finos.legend.pure.m3.execution.test.TestCollection;
Expand Down Expand Up @@ -81,7 +82,7 @@ public PureSession(SourceLocationConfiguration sourceLocationConfiguration, Muta

this.repos = Lists.mutable.withAll(repos).with(new WelcomeCodeStorage(Paths.get(rootPath)));

this.functionExecution = new FunctionExecutionInterpreted(VoidExecutionActivityListener.VOID_EXECUTION_ACTIVITY_LISTENER);
this.functionExecution = new FunctionExecutionInterpretedWithDebugSupport();

for (String property : System.getProperties().stringPropertyNames())
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2024 Goldman Sachs
~
~ Licensed 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.
~
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-ide</artifactId>
<version>4.67.9-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>legend-engine-pure-ide-light-interpreted-functions</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-ide-light-pure</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.finos.legend.pure</groupId>
<artifactId>legend-pure-m4</artifactId>
</dependency>
<dependency>
<groupId>org.finos.legend.pure</groupId>
<artifactId>legend-pure-m3-core</artifactId>
</dependency>

<dependency>
<groupId>org.finos.legend.pure</groupId>
<artifactId>legend-pure-runtime-java-engine-interpreted</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
</dependency>

</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2024 Goldman Sachs
//
// Licensed 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.
//

package org.finos.legend.engine.pure.ide.interpreted;

import org.eclipse.collections.impl.factory.Lists;
import org.eclipse.collections.impl.tuple.Tuples;
import org.finos.legend.engine.pure.ide.interpreted.debug.DebugPureIDE;
import org.finos.legend.pure.runtime.java.interpreted.extension.BaseInterpretedExtension;
import org.finos.legend.pure.runtime.java.interpreted.extension.InterpretedExtension;

public class PureIDEExtensionInterpreted extends BaseInterpretedExtension
{
public PureIDEExtensionInterpreted()
{
super(Lists.mutable.with(
Tuples.pair("debug__Nil_0_", DebugPureIDE::new)
));
}

public static InterpretedExtension extension()
{
return new PureIDEExtensionInterpreted();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2024 Goldman Sachs
//
// Licensed 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.
//

package org.finos.legend.engine.pure.ide.interpreted.debug;

import java.util.concurrent.CountDownLatch;
import org.eclipse.collections.api.list.ListIterable;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.stack.MutableStack;
import org.finos.legend.pure.m3.compiler.Context;
import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.valuespecification.InstanceValue;
import org.finos.legend.pure.m3.exception.PureAssertFailException;
import org.finos.legend.pure.m3.exception.PureExecutionException;
import org.finos.legend.pure.m3.navigation.Instance;
import org.finos.legend.pure.m3.navigation.M3Paths;
import org.finos.legend.pure.m3.navigation.M3Properties;
import org.finos.legend.pure.m3.navigation.PrimitiveUtilities;
import org.finos.legend.pure.m3.navigation.ProcessorSupport;
import org.finos.legend.pure.m3.navigation.ValueSpecificationBootstrap;
import org.finos.legend.pure.m4.ModelRepository;
import org.finos.legend.pure.m4.coreinstance.CoreInstance;
import org.finos.legend.pure.runtime.java.interpreted.ExecutionSupport;
import org.finos.legend.pure.runtime.java.interpreted.FunctionExecutionInterpreted;
import org.finos.legend.pure.runtime.java.interpreted.VariableContext;
import org.finos.legend.pure.runtime.java.interpreted.natives.InstantiationContext;
import org.finos.legend.pure.runtime.java.interpreted.natives.NativeFunction;
import org.finos.legend.pure.runtime.java.interpreted.profiler.Profiler;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Stack;

public class DebugPureIDE extends NativeFunction
{
private final FunctionExecutionInterpreted functionExecution;

private final ModelRepository repository;

public DebugPureIDE(FunctionExecutionInterpreted functionExecution, ModelRepository modelRepository)
{
this.functionExecution = functionExecution;
this.repository = modelRepository;
}

@Override
public CoreInstance execute(ListIterable<? extends CoreInstance> params, Stack<MutableMap<String, CoreInstance>> resolvedTypeParameters, Stack<MutableMap<String, CoreInstance>> resolvedMultiplicityParameters, VariableContext variableContext, MutableStack<CoreInstance> functionExpressionCallStack, Profiler profiler, InstantiationContext instantiationContext, ExecutionSupport executionSupport, Context context, ProcessorSupport processorSupport) throws PureExecutionException
{
if (this.functionExecution instanceof FunctionExecutionInterpretedWithDebugSupport)
{
FunctionExecutionInterpretedWithDebugSupport debugSupport = (FunctionExecutionInterpretedWithDebugSupport) this.functionExecution;
DebugState state = new DebugState(
debugSupport,
variableContext.getParent(), // get out of the debug function...
functionExpressionCallStack
);
state.debug();

if (state.aborted())
{
throw new PureExecutionException("Aborting execution...");
}
}

return ValueSpecificationBootstrap.newBooleanLiteral(this.repository, true, this.functionExecution.getProcessorSupport());
}
}
Loading

0 comments on commit d2e84ca

Please sign in to comment.