diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/pom.xml b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/pom.xml index 5d4f54867b6..1303297b1d0 100644 --- a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/pom.xml +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/pom.xml @@ -118,7 +118,11 @@ legend-engine-pure-ide-light-pure runtime - + + org.finos.legend.engine + legend-engine-pure-ide-light-interpreted-functions + ${project.version} + org.finos.legend.engine diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDEServer.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDEServer.java index e88af292e41..989a8d68e98 100644 --- a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDEServer.java +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/PureIDEServer.java @@ -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; @@ -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)); diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/api/debug/Debugging.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/api/debug/Debugging.java new file mode 100644 index 00000000000..2d14bf35907 --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/api/debug/Debugging.java @@ -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 args = (List) 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 getText(String value) + { + return Maps.fixedSize.of("text", value); + } +} diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/session/PureSession.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/session/PureSession.java index 4fe72148b92..1c72279d37d 100644 --- a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/session/PureSession.java +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-http-server/src/main/java/org/finos/legend/engine/ide/session/PureSession.java @@ -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; @@ -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()) { diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/pom.xml b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/pom.xml new file mode 100644 index 00000000000..96849249bab --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/pom.xml @@ -0,0 +1,61 @@ + + + + + + org.finos.legend.engine + legend-engine-pure-ide + 4.67.9-SNAPSHOT + + 4.0.0 + + legend-engine-pure-ide-light-interpreted-functions + jar + + + + org.finos.legend.engine + legend-engine-pure-ide-light-pure + runtime + + + + org.finos.legend.pure + legend-pure-m4 + + + org.finos.legend.pure + legend-pure-m3-core + + + + org.finos.legend.pure + legend-pure-runtime-java-engine-interpreted + + + + org.eclipse.collections + eclipse-collections-api + + + org.eclipse.collections + eclipse-collections + + + + diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/PureIDEExtensionInterpreted.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/PureIDEExtensionInterpreted.java new file mode 100644 index 00000000000..9414cd46880 --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/PureIDEExtensionInterpreted.java @@ -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(); + } +} diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/DebugPureIDE.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/DebugPureIDE.java new file mode 100644 index 00000000000..1020979e59e --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/DebugPureIDE.java @@ -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 params, Stack> resolvedTypeParameters, Stack> resolvedMultiplicityParameters, VariableContext variableContext, MutableStack 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()); + } +} diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/DebugState.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/DebugState.java new file mode 100644 index 00000000000..6508e4e1c92 --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/DebugState.java @@ -0,0 +1,148 @@ +// 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.io.ByteArrayOutputStream; +import java.util.concurrent.CountDownLatch; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.list.ListIterable; +import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.api.stack.MutableStack; +import org.eclipse.collections.api.tuple.Pair; +import org.eclipse.collections.impl.tuple.Tuples; +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.ProcessorSupport; +import org.finos.legend.pure.m3.navigation.generictype.GenericType; +import org.finos.legend.pure.m3.navigation.multiplicity.Multiplicity; +import org.finos.legend.pure.m3.serialization.runtime.IncrementalCompiler; +import org.finos.legend.pure.m3.serialization.runtime.Source; +import org.finos.legend.pure.m4.coreinstance.CoreInstance; +import org.finos.legend.pure.m4.transaction.framework.ThreadLocalTransactionContext; +import org.finos.legend.pure.runtime.java.interpreted.VariableContext; + +public class DebugState +{ + private final CountDownLatch latch; + + private final MutableStack functionExpressionCallStack; + + private final FunctionExecutionInterpretedWithDebugSupport debugSupport; + private final MutableList> variables; + private final String variablesTypeAndMultiplicity; + private volatile boolean abort; + + public DebugState(FunctionExecutionInterpretedWithDebugSupport debugSupport, VariableContext variableContext, MutableStack functionExpressionCallStack) + { + this.debugSupport = debugSupport; + this.latch = new CountDownLatch(1); + this.functionExpressionCallStack = functionExpressionCallStack; + this.variables = computeVariables(variableContext); + this.variablesTypeAndMultiplicity = computeVariablesTypeAndMultiplicity(this.debugSupport, this.variables); + this.debugSupport.setDebugState(this); + } + + public void release() + { + this.debugSupport.setDebugState(null); + this.latch.countDown(); + } + + public void debug() + { + try + { + this.latch.await(); + } + catch (Exception e) + { + // todo? + } + } + + public void abort() + { + this.abort = true; + this.release(); + } + + public boolean aborted() + { + return this.abort; + } + + public String getSummary() + { + PureExecutionException debugLocation = new PureExecutionException(this.functionExpressionCallStack.isEmpty() ? null : this.functionExpressionCallStack.peek().getSourceInformation(), "debug location", functionExpressionCallStack); + StringBuilder appendable = new StringBuilder(); + debugLocation.printPureStackTrace(appendable, "", this.debugSupport.getProcessorSupport()); + return "Variables: " + this.variablesTypeAndMultiplicity + "\n\n" + appendable; + } + + public String evaluate(String command) + { + Source inMemoryCodeBlock = this.debugSupport.getPureRuntime().createInMemoryCodeBlock("{" + variablesTypeAndMultiplicity + "|\n" + command + "\n}"); + + IncrementalCompiler incrementalCompiler = this.debugSupport.getPureRuntime().getIncrementalCompiler(); + IncrementalCompiler.IncrementalCompilerTransaction transaction = incrementalCompiler.newTransaction(false); + try (ThreadLocalTransactionContext ignore = transaction.openInCurrentThread()) + { + incrementalCompiler.compileInCurrentTransaction(inMemoryCodeBlock); + } + + ListIterable newInstances = inMemoryCodeBlock.getNewInstances(); + + CoreInstance result = this.debugSupport.startRaw(newInstances.get(0), Lists.fixedSize.of()); + CoreInstance lambda = Instance.getValueForMetaPropertyToOneResolved(result, M3Properties.values, this.debugSupport.getProcessorSupport()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + this.debugSupport.start(lambda, variables.collect(Pair::getTwo), out, this.debugSupport.newOutputWriter()); + return out.toString(); + } + + private static String computeVariablesTypeAndMultiplicity(FunctionExecutionInterpretedWithDebugSupport debugSupport, MutableList> variables) + { + return variables.collect(x -> x.getOne() + ":" + computeVariableTypeAndMultiplicity(debugSupport, x.getTwo())).makeString(", "); + } + + private static MutableList> computeVariables(VariableContext variableContext) + { + return variableContext.getVariableNames() + .asLazy() + .collect(x -> Tuples.pair(x, variableContext.getValue(x))).select(x -> x.getTwo() != null) + .toList(); + } + + private static String computeVariableTypeAndMultiplicity(FunctionExecutionInterpretedWithDebugSupport debugSupport, CoreInstance coreInstance) + { + // todo the GenericType.print has a bug with type arguments, and functions get printed wrong! + // ie. meta::pure::metamodel::function::ConcreteFunctionDefinition< {meta::pure::metamodel::function::Function<{->X[o]}>[1]->X[o]}> + String type; + ProcessorSupport processorSupport = debugSupport.getProcessorSupport(); + if (processorSupport.type_subTypeOf(coreInstance.getValueForMetaPropertyToOne(M3Properties.genericType).getValueForMetaPropertyToOne(M3Properties.rawType), debugSupport.getPureRuntime().getCoreInstance(M3Paths.Function))) + { + type = "Function"; + } + else + { + type = GenericType.print(coreInstance.getValueForMetaPropertyToOne(M3Properties.genericType), true, processorSupport); + } + String multiplicity = Multiplicity.print(coreInstance.getValueForMetaPropertyToOne(M3Properties.multiplicity)); + return type + multiplicity; + } +} diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/FunctionExecutionInterpretedWithDebugSupport.java b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/FunctionExecutionInterpretedWithDebugSupport.java new file mode 100644 index 00000000000..1641414605f --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/java/org/finos/legend/engine/pure/ide/interpreted/debug/FunctionExecutionInterpretedWithDebugSupport.java @@ -0,0 +1,122 @@ +// 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.io.IOException; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import org.eclipse.collections.api.list.ListIterable; +import org.finos.legend.pure.m3.exception.PureExecutionException; +import org.finos.legend.pure.m3.execution.OutputWriter; +import org.finos.legend.pure.m3.navigation.M3Properties; +import org.finos.legend.pure.m3.navigation.ValueSpecificationBootstrap; +import org.finos.legend.pure.m3.statelistener.VoidExecutionActivityListener; +import org.finos.legend.pure.m4.coreinstance.CoreInstance; +import org.finos.legend.pure.runtime.java.interpreted.FunctionExecutionInterpreted; + +public class FunctionExecutionInterpretedWithDebugSupport extends FunctionExecutionInterpreted +{ + private volatile CompletableFuture currentExecution; + private volatile CompletableFuture resultHandler; + private volatile DebugState debugState; + + public FunctionExecutionInterpretedWithDebugSupport() + { + super(VoidExecutionActivityListener.VOID_EXECUTION_ACTIVITY_LISTENER); + } + + public DebugState getDebugState() + { + return this.debugState; + } + + public void setDebugState(DebugState debugState) + { + if (debugState != null && this.debugState != null) + { + throw new IllegalStateException("Debug session already exists?"); + } + + this.debugState = debugState; + + if (debugState != null) + { + this.getConsole().print("Entering debug mode. Use terminal to introspect debug state. F9 to continue execution.\n\nDebug summary:\n"); + this.getConsole().print(debugState.getSummary()); + this.resultHandler.complete(null); + } + } + + @Override + public CoreInstance start(CoreInstance function, ListIterable arguments) + { + this.resultHandler = new CompletableFuture<>(); + + if (this.currentExecution == null) + { + this.currentExecution = CompletableFuture.supplyAsync(() -> this.startRaw(function, arguments)); + this.currentExecution.whenComplete((v, e) -> + { + if (e != null) + { + this.resultHandler.completeExceptionally(e); + } + else + { + this.resultHandler.complete(v); + } + this.currentExecution = null; + }); + } + else + { + this.debugState.release(); + } + + try + { + return this.resultHandler.join(); + } + catch (CompletionException e) + { + throw (RuntimeException) e.getCause(); + } + } + + public CoreInstance startRaw(CoreInstance function, ListIterable arguments) + { + return super.start(function, arguments); + } + + @Override + public void start(CoreInstance func, ListIterable arguments, OutputStream + outputStream, OutputWriter writer) + { + CoreInstance result = this.startRaw(func, arguments); + + try + { + ListIterable values = result.getValueForMetaPropertyToMany(M3Properties.values); + writer.write(values, outputStream); + } + catch (IOException e) + { + throw new UncheckedIOException("Failed to write to output stream", e); + } + } +} diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/resources/META-INF/services/org.finos.legend.pure.runtime.java.interpreted.extension.InterpretedExtension b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/resources/META-INF/services/org.finos.legend.pure.runtime.java.interpreted.extension.InterpretedExtension new file mode 100644 index 00000000000..5c1cbdf66fd --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-interpreted-functions/src/main/resources/META-INF/services/org.finos.legend.pure.runtime.java.interpreted.extension.InterpretedExtension @@ -0,0 +1 @@ +org.finos.legend.engine.pure.ide.interpreted.PureIDEExtensionInterpreted \ No newline at end of file diff --git a/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-pure/src/main/resources/pure_ide/debug.pure b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-pure/src/main/resources/pure_ide/debug.pure new file mode 100644 index 00000000000..59a0bb9f3b9 --- /dev/null +++ b/legend-engine-pure/legend-engine-pure-ide/legend-engine-pure-ide-light-pure/src/main/resources/pure_ide/debug.pure @@ -0,0 +1,15 @@ +// 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. + +native function meta::pure::ide::debug():Nil[0]; \ No newline at end of file diff --git a/legend-engine-pure/legend-engine-pure-ide/pom.xml b/legend-engine-pure/legend-engine-pure-ide/pom.xml index ecea78d015a..5352a031d93 100644 --- a/legend-engine-pure/legend-engine-pure-ide/pom.xml +++ b/legend-engine-pure/legend-engine-pure-ide/pom.xml @@ -30,5 +30,6 @@ legend-engine-pure-ide-light-http-server legend-engine-pure-ide-light-metadata-pure legend-engine-pure-ide-light-pure + legend-engine-pure-ide-light-interpreted-functions \ No newline at end of file