diff --git a/.gitignore b/.gitignore index 0483f5409..201e4ab1a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ traces /tests/streams/*.txt npm-debug.log* /.metadata +.DS_Store +/SOMns.iml +/libs/black-diamonds diff --git a/build.xml b/build.xml index 3fec34f04..d72c556f4 100644 --- a/build.xml +++ b/build.xml @@ -529,4 +529,14 @@ kernel: ${kernel} + + + + + + + + + + diff --git a/src/som/VM.java b/src/som/VM.java index 90f5e59fc..22390d4c8 100644 --- a/src/som/VM.java +++ b/src/som/VM.java @@ -409,6 +409,8 @@ public void setupInstruments(final Env env) { : "Currently, CandidateIdentifer and Snapshots are not compatible"; structuralProbe = SnapshotBackend.getProbe(); } + + structuralProbe = new StructuralProbe<>(); } public SClass loadExtensionModule(final String filename) { @@ -419,6 +421,13 @@ public MixinDefinition loadModule(final String filename) throws IOException { return objectSystem.loadModule(filename); } + public MixinDefinition loadModule(final String filename,MixinDefinition oldModule) throws IOException { + if (oldModule == null) { + return objectSystem.loadModule(filename); + } + return objectSystem.reLoadModule(filename,oldModule); + } + public MixinDefinition loadModule(final Source source) throws IOException { return objectSystem.loadModule(source); } @@ -443,4 +452,8 @@ public static void resetClassReferences(final boolean callFromUnitTest) { ChannelPrimitives.resetClassReferences(); } + + public StructuralProbe getStructuralProbe(){ + return structuralProbe; + } } diff --git a/src/som/compiler/MixinBuilder.java b/src/som/compiler/MixinBuilder.java index 458ae6b43..7f7b57365 100644 --- a/src/som/compiler/MixinBuilder.java +++ b/src/som/compiler/MixinBuilder.java @@ -157,6 +157,29 @@ public MixinBuilder(final ScopeBuilder outer, final AccessModifier accessModi this.structuralProbe = structuralProbe; } + public MixinBuilder(final ScopeBuilder outer, final AccessModifier accessModifier, + final SSymbol name, final SourceSection nameSection, + final StructuralProbe structuralProbe, + final SomLanguage language, final MixinDefinitionId mixinId){ + super(outer, outer == null ? null : outer.getScope()); + this.name = name; + this.nameSection = nameSection; + this.mixinId = mixinId; + + this.classSide = false; + this.language = language; + + this.classScope = createScope(scope); + + this.initializer = new MethodBuilder(this, structuralProbe); + this.primaryFactoryMethod = + new MethodBuilder(this, classScope, false, language, structuralProbe); + this.superclassAndMixinResolutionBuilder = createSuperclassResolutionBuilder(); + + this.accessModifier = accessModifier; + this.structuralProbe = structuralProbe; + } + public static class MixinDefinitionError extends SemanticDefinitionError { private static final long serialVersionUID = 5030639383869198851L; @@ -493,7 +516,7 @@ protected boolean isImmutable() { return true; } - private void setHolders(final MixinDefinition clsDef) { + public void setHolders(final MixinDefinition clsDef) { assert clsDef != null; for (Dispatchable disp : dispatchables.getValues()) { if (disp instanceof SInvokable) { @@ -675,6 +698,10 @@ public void addNestedMixin(final MixinDefinition nestedMixin) slots.put(name, cacheSlot); } + public void addSlotsUnsafe(EconomicMap newSlots){ + slots.putAll(newSlots); + } + public MixinDefinitionId getMixinId() { return mixinId; } diff --git a/src/som/compiler/MixinDefinition.java b/src/som/compiler/MixinDefinition.java index c12ad50ea..86e0892a8 100644 --- a/src/som/compiler/MixinDefinition.java +++ b/src/som/compiler/MixinDefinition.java @@ -291,7 +291,7 @@ protected static boolean sameClassConstruction(final Object a, final Object b) { return sameClassConstruction(aC.getSuperClass(), bC.getSuperClass()); } - private final ArrayList cache = new ArrayList<>(2); + public final ArrayList cache = new ArrayList<>(2); private ClassFactory getCached(final Object superclassAndMixins) { if (superclassAndMixins == null) { @@ -907,4 +907,5 @@ public SSymbol getIdentifier() { return identifier; } + } diff --git a/src/som/compiler/Parser.java b/src/som/compiler/Parser.java index b5324953b..a134e67ed 100644 --- a/src/som/compiler/Parser.java +++ b/src/som/compiler/Parser.java @@ -335,6 +335,13 @@ public MixinBuilder moduleDeclaration() throws ProgramDefinitionError { return classDeclaration(null, AccessModifier.PUBLIC); } + public MixinBuilder moduleDeclaration(MixinBuilder.MixinDefinitionId mixinDefinitionId) throws ProgramDefinitionError { + compatibilityNewspeakVersionAndFileCategory(); + + comments(); + return classDeclaration(null, AccessModifier.PUBLIC, mixinDefinitionId); + } + protected String className() throws ParseError { String mixinName = text; expect(Identifier, IdentifierTag.class); @@ -380,6 +387,45 @@ private MixinBuilder classDeclaration(final MixinBuilder outerBuilder, return mxnBuilder; } + private MixinBuilder classDeclaration(final MixinBuilder outerBuilder, + final AccessModifier accessModifier, MixinBuilder.MixinDefinitionId mixinDefinitionId) throws ProgramDefinitionError { + expectIdentifier("class", "Found unexpected token %(found)s. " + + "Tried parsing a class declaration and expected 'class' instead.", + KeywordTag.class); + + int coord = getStartIndex(); + String mixinName = className(); + SourceSection nameSS = getSource(coord); + + MixinBuilder mxnBuilder = new MixinBuilder(outerBuilder, accessModifier, + symbolFor(mixinName), nameSS, structuralProbe, language,mixinDefinitionId); + + MethodBuilder primaryFactory = mxnBuilder.getPrimaryFactoryMethodBuilder(); + coord = getStartIndex(); + + // Newspeak-spec: this is not strictly sufficient for Newspeak + // it could also parse a binary selector here, I think + // but, doesn't seem so useful, so, let's keep it simple + if (sym == Identifier || sym == Keyword) { + messagePattern(primaryFactory); + } else { + // in the standard case, the primary factory method is #new + primaryFactory.addArgument(Symbols.SELF, getEmptySource()); + primaryFactory.setSignature(Symbols.NEW); + } + mxnBuilder.setupInitializerBasedOnPrimaryFactory(getSource(coord)); + + comments(); + + expect(Equal, "Unexpected symbol %(found)s." + + " Tried to parse the class declaration of " + mixinName + + " and expect '=' before the (optional) inheritance declaration.", + KeywordTag.class); + + inheritanceListAndOrBody(mxnBuilder); + return mxnBuilder; + } + private void inheritanceListAndOrBody(final MixinBuilder mxnBuilder) throws ProgramDefinitionError { if (sym == NewTerm) { diff --git a/src/som/compiler/SourcecodeCompiler.java b/src/som/compiler/SourcecodeCompiler.java index 0d26ffad2..57eed172f 100644 --- a/src/som/compiler/SourcecodeCompiler.java +++ b/src/som/compiler/SourcecodeCompiler.java @@ -62,4 +62,21 @@ protected final MixinDefinition compile(final Parser parser, language.getVM().reportLoadedSource(source); return result; } + + public MixinDefinition recompileModule(Source source, StructuralProbe structuralProbe) throws ProgramDefinitionError { + Parser parser = new Parser(source.getCharacters().toString(), source, null, language); + int coord = parser.getStartIndex(); + MixinBuilder mxnBuilder = parser.moduleDeclaration(); + MixinDefinition result = mxnBuilder.assemble(parser.getSource(coord)); + return result; + } + + public MixinDefinition recompileModule(Source source, StructuralProbe structuralProbe, MixinDefinition oldModule) throws ProgramDefinitionError { + Parser parser = new Parser(source.getCharacters().toString(), source, null, language); + int coord = parser.getStartIndex(); + MixinBuilder mxnBuilder = parser.moduleDeclaration(oldModule.getMixinId()); + //mxnBuilder.addSlotsUnsafe(oldModule.getSlots()); + MixinDefinition result = mxnBuilder.assemble(parser.getSource(coord)); + return result; + } } diff --git a/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java b/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java index 51109b072..96f6c769c 100644 --- a/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java +++ b/src/som/interpreter/nodes/dispatch/ClassSlotAccessNode.java @@ -87,6 +87,9 @@ public SClass read(final SObject rcvr) { if (cachedValue == Nil.nilObject) { return instantiateAndWriteUnsynced(rcvr); } else { + if (cachedValue == null) { + return instantiateAndWriteUnsynced(rcvr); + } assert cachedValue instanceof SClass; return (SClass) cachedValue; } diff --git a/src/som/interpreter/nodes/dispatch/DispatchGuard.java b/src/som/interpreter/nodes/dispatch/DispatchGuard.java index 6b5129b3b..5f1b753f9 100644 --- a/src/som/interpreter/nodes/dispatch/DispatchGuard.java +++ b/src/som/interpreter/nodes/dispatch/DispatchGuard.java @@ -1,5 +1,9 @@ package som.interpreter.nodes.dispatch; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleRuntime; import com.oracle.truffle.api.nodes.InvalidAssumptionException; import som.compiler.MixinDefinition.SlotDefinition; @@ -14,6 +18,15 @@ public abstract class DispatchGuard { + + @CompilerDirectives.CompilationFinal + private static Assumption noModuleReloaded = Truffle.getRuntime().createAssumption("noMoRel"); + + public static void invalidateAssumption() { + noModuleReloaded.invalidate(); + noModuleReloaded = Truffle.getRuntime().createAssumption("noMoRel"); + } + public abstract boolean entryMatches(Object obj) throws InvalidAssumptionException; @@ -58,6 +71,7 @@ public static CheckSObject createSObjectCheck(final SObject obj) { private static final class CheckClass extends DispatchGuard { private final Class expected; + private final Assumption noLoad = noModuleReloaded; CheckClass(final Class expectedClass) { this.expected = expectedClass; @@ -65,20 +79,29 @@ private static final class CheckClass extends DispatchGuard { @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); return obj.getClass() == expected; } } private static final class CheckTrue extends DispatchGuard { + + private final Assumption noLoad = noModuleReloaded; + @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); return obj == Boolean.TRUE; } } private static final class CheckFalse extends DispatchGuard { + + private final Assumption noLoad = noModuleReloaded; + @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); return obj == Boolean.FALSE; } } @@ -86,6 +109,7 @@ public boolean entryMatches(final Object obj) throws InvalidAssumptionException private static final class CheckObjectWithoutFields extends DispatchGuard { private final ClassFactory expected; + private final Assumption noLoad = noModuleReloaded; CheckObjectWithoutFields(final ClassFactory expected) { this.expected = expected; @@ -93,6 +117,7 @@ private static final class CheckObjectWithoutFields extends DispatchGuard { @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); return obj instanceof SObjectWithoutFields && ((SObjectWithoutFields) obj).getFactory() == expected; } @@ -101,6 +126,7 @@ public boolean entryMatches(final Object obj) throws InvalidAssumptionException private static final class CheckSClass extends DispatchGuard { private final ClassFactory expected; + private final Assumption noLoad = noModuleReloaded; CheckSClass(final ClassFactory expected) { this.expected = expected; @@ -108,6 +134,7 @@ private static final class CheckSClass extends DispatchGuard { @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); return obj instanceof SClass && ((SClass) obj).getFactory() == expected; } @@ -115,6 +142,7 @@ public boolean entryMatches(final Object obj) throws InvalidAssumptionException public abstract static class CheckSObject extends DispatchGuard { protected final ObjectLayout expected; + protected final Assumption noLoad = noModuleReloaded; CheckSObject(final ObjectLayout expected) { this.expected = expected; @@ -136,6 +164,7 @@ private static final class CheckSMutableObject extends CheckSObject { @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); expected.checkIsLatest(); return obj instanceof SMutableObject && ((SMutableObject) obj).getObjectLayout() == expected; @@ -155,6 +184,7 @@ private static final class CheckSImmutableObject extends CheckSObject { @Override public boolean entryMatches(final Object obj) throws InvalidAssumptionException { + noLoad.check(); expected.checkIsLatest(); return obj instanceof SImmutableObject && ((SImmutableObject) obj).getObjectLayout() == expected; diff --git a/src/som/interpreter/objectstorage/ClassFactory.java b/src/som/interpreter/objectstorage/ClassFactory.java index 639764042..4bcda3bbe 100644 --- a/src/som/interpreter/objectstorage/ClassFactory.java +++ b/src/som/interpreter/objectstorage/ClassFactory.java @@ -53,7 +53,7 @@ public final class ClassFactory { private final MixinDefinition mixinDef; private final EconomicSet instanceSlots; - private final EconomicMap dispatchables; + public final EconomicMap dispatchables; private final boolean hasOnlyImmutableFields; diff --git a/src/som/interpreter/objectstorage/StorageLocation.java b/src/som/interpreter/objectstorage/StorageLocation.java index a933cddf0..0291c0ed6 100644 --- a/src/som/interpreter/objectstorage/StorageLocation.java +++ b/src/som/interpreter/objectstorage/StorageLocation.java @@ -54,16 +54,20 @@ public static StorageLocation createForObject(final ObjectLayout layout, } protected final ObjectLayout layout; - protected final SlotDefinition slot; + protected SlotDefinition slot; protected StorageLocation(final ObjectLayout layout, final SlotDefinition slot) { this.layout = layout; this.slot = slot; } + public SlotDefinition getSlot() { return slot; } + public void setSlot(SlotDefinition slot) { + this.slot = slot; + } /** * @return true, if it is an object location, false otherwise. diff --git a/src/som/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java index abf9a8ce3..0cf465481 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -150,10 +150,39 @@ public MixinDefinition loadModule(final String filename) throws IOException { return loadModule(source); } + public MixinDefinition reLoadModule(final String filename, MixinDefinition oldModule) throws IOException{ + File file = new File(filename); + + if (!file.exists()) { + throw new FileNotFoundException(filename); + } + + if (!file.isFile()) { + throw new NotAFileException(filename); + } + + Source source = SomLanguage.getSource(file); + return reLoadModule(source,oldModule); + } + + public MixinDefinition reLoadModule(final Source source, MixinDefinition oldModule) throws IOException { + try { + return compiler.recompileModule(source,null,oldModule); + } catch (ProgramDefinitionError programDefinitionError) { + programDefinitionError.printStackTrace(); + } + return null; + } + public MixinDefinition loadModule(final Source source) throws IOException { URI uri = source.getURI(); if (loadedModules.containsKey(uri)) { - return loadedModules.get(uri); + System.out.println("UPDATING MODULE"); + try { + return compiler.recompileModule(source,null); + } catch (ProgramDefinitionError programDefinitionError) { + programDefinitionError.printStackTrace(); + } } MixinDefinition module; diff --git a/src/som/vmobjects/SObject.java b/src/som/vmobjects/SObject.java index a12f4ee87..d793b6881 100644 --- a/src/som/vmobjects/SObject.java +++ b/src/som/vmobjects/SObject.java @@ -25,6 +25,8 @@ package som.vmobjects; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; @@ -34,9 +36,12 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.nodes.ExplodeLoop; +import org.graalvm.collections.Pair; import som.compiler.MixinDefinition.SlotDefinition; +import som.interpreter.nodes.dispatch.DispatchGuard; import som.interpreter.objectstorage.ClassFactory; import som.interpreter.objectstorage.ObjectLayout; +import som.interpreter.objectstorage.ObjectTransitionSafepoint; import som.interpreter.objectstorage.StorageLocation; import som.interpreter.objectstorage.StorageLocation.DoubleStorageLocation; import som.interpreter.objectstorage.StorageLocation.LongStorageLocation; @@ -226,7 +231,7 @@ private boolean txMutObjLocEquals(final SMutableObject o) { // need to ignore mutators and class slots // ignore primitives, have been checked separately before if (e.getKey().getClass() == SlotDefinition.class && e.getValue().isObjectLocation() && - e.getValue().read(this) != oLocs.get(e.getKey()).read(o)) { + e.getValue().read(this) != oLocs.get(e.getKey()).read(this)) { return false; } } @@ -394,7 +399,6 @@ private EconomicMap getAllFields() { private void setAllFields(final EconomicMap fieldValues) { resetFields(); primitiveUsedMap = 0; - MapCursor entry = fieldValues.getEntries(); while (entry.advance()) { if (entry.getValue() != null) { @@ -424,12 +428,56 @@ private void setLayoutAndTransferFields(final ObjectLayout layoutAtClass) { CompilerDirectives.transferToInterpreterAndInvalidate(); EconomicMap fieldValues = getAllFields(); + //fieldValues = fixFieldsToNewSlots(layoutAtClass, fieldValues); + ObjectLayout oldObjectLayout = objectLayout; + objectLayout = layoutAtClass; + assert oldObjectLayout != objectLayout; + extensionPrimFields = getExtendedPrimStorage(layoutAtClass); + extensionObjFields = getExtendedObjectStorage(layoutAtClass); + try { + setAllFields(fieldValues); + } catch ( ArithmeticException error){ + objectLayout = oldObjectLayout; + this.setLayoutAndTransferFieldsOnClassUpdate(layoutAtClass, fieldValues); + } + } + private void setLayoutAndTransferFieldsOnClassUpdate(final ObjectLayout layoutAtClass,EconomicMap fieldValues) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + EconomicMap newSlots = fixFieldsToNewSlots(layoutAtClass, fieldValues); objectLayout = layoutAtClass; extensionPrimFields = getExtendedPrimStorage(layoutAtClass); extensionObjFields = getExtendedObjectStorage(layoutAtClass); - setAllFields(fieldValues); + setAllFields(newSlots); + DispatchGuard.invalidateAssumption(); + } + + private EconomicMap fixFieldsToNewSlots(ObjectLayout layoutAtClass, EconomicMap fieldValues ) { + EconomicMap oldSlots = objectLayout.getStorageLocations(); + EconomicMap newSlots = layoutAtClass.getStorageLocations(); + Map> layoutLocations = new HashMap<>(); + EconomicMap newFieldValues = EconomicMap.create(); + for (SlotDefinition newSlot : newSlots.getKeys()){ + for (SlotDefinition oldSlot : oldSlots.getKeys()){ + if(oldSlot.getName() == newSlot.getName()){ + StorageLocation newLoc = newSlots.get(newSlot); + if (newLoc.isObjectLocation()){ + layoutLocations.put(oldSlot, Pair.create(newSlot,fieldValues.get(oldSlot))); + break; + } + } + } + + } + for (SlotDefinition slot : fieldValues.getKeys()) { + //StorageLocation location = oldSlots.get(slot); + Pair newSlotDef = layoutLocations.get(slot); + if (newSlotDef != null) { + newFieldValues.put(newSlotDef.getLeft(),newSlotDef.getRight()); + } + } + return newFieldValues; } /** @@ -483,7 +531,12 @@ public static int getPrimitiveFieldMask(final int fieldIndex) { private StorageLocation getLocation(final SlotDefinition slot) { StorageLocation location = objectLayout.getStorageLocation(slot); - assert location != null; + // TODO: added here a division by 0 to be able to debug this when it happens. + // I do not recall what it is due to, but probably it is about reloading nested classes + // assert location != null; + if (location == null) { + int a = 1 / 0; + } return location; } diff --git a/src/tools/debugger/FrontendConnector.java b/src/tools/debugger/FrontendConnector.java index 98522f88f..8de15643f 100644 --- a/src/tools/debugger/FrontendConnector.java +++ b/src/tools/debugger/FrontendConnector.java @@ -4,14 +4,17 @@ import java.net.BindException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; +import com.oracle.truffle.api.debug.DebugStackFrame; +import com.oracle.truffle.api.debug.DebugValue; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.EconomicSet; import org.java_websocket.WebSocket; import com.google.gson.Gson; @@ -24,7 +27,16 @@ import bd.source.SourceCoordinate; import bd.source.TaggedSourceCoordinate; +import bd.tools.structure.StructuralProbe; +import som.VM; +import som.compiler.MixinDefinition; +import som.compiler.Variable; +import som.interpreter.nodes.dispatch.DispatchGuard; +import som.interpreter.nodes.dispatch.Dispatchable; +import som.interpreter.objectstorage.ClassFactory; +import som.interpreter.objectstorage.StorageLocation; import som.vm.VmSettings; +import som.vmobjects.SInvokable; import som.vmobjects.SSymbol; import tools.Tagging; import tools.TraceData; @@ -189,6 +201,7 @@ private void ensureConnectionIsAvailable() { private static TaggedSourceCoordinate[] createSourceSections(final Source source, final Map>>> sourcesTags, final Instrumenter instrumenter, final Set rootNodes) { + Set sections = new HashSet<>(); Map>> tagsForSections = sourcesTags.get(source); @@ -349,4 +362,78 @@ private void closeAllSockets() { traceHandler.stop(delay); } catch (InterruptedException e) {} } + + public void restartFrame(Suspension suspension, DebugStackFrame frame) { + suspension.getEvent().prepareUnwindFrame(frame,null) ; + suspension.resume(); + suspension.resume(); + } + + // Code Uptading stuff + public MixinDefinition updateClass(String filePath) { + try { + //TODO: support inner classes + VM vm = webDebugger.vm; + StructuralProbe structuralProbe = vm.getStructuralProbe(); + EconomicSet modules = structuralProbe.getClasses(); + MixinDefinition oldModule = null; + Path newSource = Paths.get(VmSettings.BASE_DIRECTORY).toAbsolutePath().relativize(Path.of(filePath)); + for(MixinDefinition module : modules ) { + if(module.getIdentifier().getString().split(":")[0].equals(newSource.toString())) { + oldModule = module; + } + } + MixinDefinition newModule = vm.loadModule(filePath,oldModule); + System.out.println(newModule.getName().toString()); + EconomicMap oldMethods = oldModule.getInstanceDispatchables(); + oldMethods.putAll(newModule.getInstanceDispatchables()); + EconomicMap newDisp = newModule.getInstanceDispatchables(); + //remove slot definition from dispatchables + for(ClassFactory module : oldModule.cache) { +// for (SSymbol key : newDisp.getKeys()){ +// if(newDisp.get(key) instanceof MixinDefinition.SlotDefinition){ +// newDisp.removeKey(key); +// } +// } + module.dispatchables.putAll(newModule.getInstanceDispatchables()); + } + + for (Dispatchable disp : oldModule.getInstanceDispatchables().getValues()) { + if (disp instanceof SInvokable){ + SInvokable inv = (SInvokable) disp; + inv.setHolder(oldModule); + } + } + oldModule.getSlots().putAll(newModule.getSlots()); + // Pushing new slots + for (MixinDefinition.SlotDefinition sl : oldModule.getSlots().getValues()){ + for(ClassFactory module : oldModule.cache) { + EconomicMap storageLocations = + module.getInstanceLayout().getStorageLocations(); + Iterable oldKeys = storageLocations.getKeys(); + Map toSubstitute = new HashMap<>(); + for (MixinDefinition.SlotDefinition oldKey : oldKeys){ + if(oldKey.getName() == sl.getName()){ + toSubstitute.put(oldKey,sl); + break; + } + } + for (Map.Entry newOld : toSubstitute.entrySet()){ + StorageLocation location = storageLocations.get(newOld.getKey()); + storageLocations.removeKey(newOld.getKey()); + storageLocations.put(newOld.getValue(),location); + location.setSlot(newOld.getValue()); + } + } + } + System.out.println("SUBSTITUTED METHODS IN MODULE"); + + DispatchGuard.invalidateAssumption(); + + return oldModule; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/src/tools/debugger/RuntimeReflectionRegistration.java b/src/tools/debugger/RuntimeReflectionRegistration.java index d32b04da1..6b7eee568 100644 --- a/src/tools/debugger/RuntimeReflectionRegistration.java +++ b/src/tools/debugger/RuntimeReflectionRegistration.java @@ -13,24 +13,9 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import tools.debugger.message.InitializationResponse; -import tools.debugger.message.InitializeConnection; +import tools.debugger.message.*; import tools.debugger.message.Message.IncommingMessage; import tools.debugger.message.Message.OutgoingMessage; -import tools.debugger.message.ProgramInfoRequest; -import tools.debugger.message.ProgramInfoResponse; -import tools.debugger.message.ScopesRequest; -import tools.debugger.message.ScopesResponse; -import tools.debugger.message.SourceMessage; -import tools.debugger.message.StackTraceRequest; -import tools.debugger.message.StackTraceResponse; -import tools.debugger.message.StepMessage; -import tools.debugger.message.StoppedMessage; -import tools.debugger.message.SymbolMessage; -import tools.debugger.message.TraceDataRequest; -import tools.debugger.message.UpdateBreakpoint; -import tools.debugger.message.VariablesRequest; -import tools.debugger.message.VariablesResponse; import tools.debugger.session.BreakpointInfo; import tools.debugger.session.LineBreakpoint; import tools.debugger.session.SectionBreakpoint; @@ -89,6 +74,8 @@ public void register(final String name, final Class klass) { inMsgs.register(VariablesRequest.class); inMsgs.register(ProgramInfoRequest.class); inMsgs.register(TraceDataRequest.class); + inMsgs.register(UpdateClass.class); + inMsgs.register(RestartFrame.class); ClassGroup bps = new ClassGroup(BreakpointInfo.class, "type", true); bps.register(LineBreakpoint.class); diff --git a/src/tools/debugger/frontend/ApplicationThreadTask.java b/src/tools/debugger/frontend/ApplicationThreadTask.java index 2b16e2045..3396b7e1d 100644 --- a/src/tools/debugger/frontend/ApplicationThreadTask.java +++ b/src/tools/debugger/frontend/ApplicationThreadTask.java @@ -7,11 +7,11 @@ * A task that needs to be executed by the application thread while it is in a * {@link Suspension}. */ -abstract class ApplicationThreadTask { +abstract public class ApplicationThreadTask { /** * @return continue waiting on true, resume execution on false. */ - abstract boolean execute(); + protected abstract boolean execute(); static class Resume extends ApplicationThreadTask { @Override diff --git a/src/tools/debugger/frontend/Suspension.java b/src/tools/debugger/frontend/Suspension.java index d701cf436..491ca2684 100644 --- a/src/tools/debugger/frontend/Suspension.java +++ b/src/tools/debugger/frontend/Suspension.java @@ -132,7 +132,7 @@ public void sendVariables(final long varRef, final FrontendConnector frontend, frontend.sendVariables(varRef, requestId, this, filter, start, count); } - private void submitTask(final ApplicationThreadTask task) { + public void submitTask(final ApplicationThreadTask task) { try { tasks.put(task); } catch (InterruptedException e) { diff --git a/src/tools/debugger/message/RestartFrame.java b/src/tools/debugger/message/RestartFrame.java new file mode 100644 index 000000000..df845bb91 --- /dev/null +++ b/src/tools/debugger/message/RestartFrame.java @@ -0,0 +1,22 @@ +package tools.debugger.message; +import org.java_websocket.WebSocket; +import tools.debugger.FrontendConnector; +import tools.debugger.frontend.Suspension; +import tools.debugger.message.Message.IncommingMessage; +public class RestartFrame extends IncommingMessage { + private final int frameId; + + public RestartFrame(int currentFrameId) { + this.frameId = currentFrameId; + } + + @Override + public void process(FrontendConnector connector, WebSocket conn) { + Suspension suspension= connector.getSuspension(0); + int skipCount = suspension.getFrameSkipCount(); + skipCount = skipCount > 0 ? skipCount - 1 : skipCount; + int realId = frameId + skipCount; + System.out.print("Restarted Frame Request " + frameId + "After skipping " + realId); + connector.restartFrame(suspension,suspension.getStackFrames().get(realId)); + } +} diff --git a/src/tools/debugger/message/StackTraceResponse.java b/src/tools/debugger/message/StackTraceResponse.java index 0adc25e83..5e5f20fea 100644 --- a/src/tools/debugger/message/StackTraceResponse.java +++ b/src/tools/debugger/message/StackTraceResponse.java @@ -74,11 +74,14 @@ private static class StackFrame { /** An optional number of characters in the range. */ private final int length; - StackFrame(final long globalId, final String name, final String sourceUri, + private final long frameId; + + StackFrame(final long globalId, final long frameId, final String name, final String sourceUri, final int line, final int column, final int endLine, final int endColumn, final int length) { assert TraceData.isWithinJSIntValueRange(globalId); this.id = globalId; + this.frameId = frameId; this.name = name; this.sourceUri = sourceUri; this.line = line; @@ -170,6 +173,6 @@ private static StackFrame createFrame(final Suspension suspension, endColumn = 0; length = 0; } - return new StackFrame(id, name, sourceUri, line, column, endLine, endColumn, length); + return new StackFrame(id, frameId, name, sourceUri, line, column, endLine, endColumn, length); } } diff --git a/src/tools/debugger/message/UpdateClass.java b/src/tools/debugger/message/UpdateClass.java new file mode 100644 index 000000000..f38a00ea7 --- /dev/null +++ b/src/tools/debugger/message/UpdateClass.java @@ -0,0 +1,83 @@ +package tools.debugger.message; + +import com.oracle.truffle.api.debug.DebugStackFrame; +import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.nodes.RootNode; +import org.java_websocket.WebSocket; +import som.compiler.MixinDefinition; +import som.interpreter.SomLanguage; +import tools.debugger.FrontendConnector; +import tools.debugger.frontend.ApplicationThreadTask; +import tools.debugger.frontend.Suspension; +import tools.debugger.message.Message.IncommingMessage; +import tools.debugger.frontend.ApplicationThreadTask; + +import java.util.ArrayList; + + +public class UpdateClass extends IncommingMessage { + private final String classToRecompile ; + + public UpdateClass(String classToRecompile) { + this.classToRecompile = classToRecompile; + } + + @Override public void process(FrontendConnector connector, WebSocket conn) { + Suspension suspension = connector.getSuspension(0); + UpdateClassTask updateTask = new UpdateClassTask(connector,suspension,classToRecompile); + suspension.submitTask(updateTask); + + //suspension.resume(); + //connector.updateClass(classToRecompile); + System.out.println("Done Recompilation"); + System.out.println(classToRecompile); + } + + private class UpdateClassTask extends ApplicationThreadTask { + + private final FrontendConnector frontend; + private final Suspension suspension; + private final String filePath; + + UpdateClassTask(final FrontendConnector frontend, final Suspension suspension, String filePath) { + this.frontend = frontend; + this.suspension = suspension; + this.filePath = filePath; + + } + + @Override + public boolean execute() { + MixinDefinition updatedModule = frontend.updateClass(filePath); + ArrayList frames = suspension.getStackFrames(); +// int a = 1 + 1; +// System.out.println(a); + DebugStackFrame restartFrame = null; + for(DebugStackFrame frame : frames){ + if(frame.getSourceSection().getSource().getURI() == updatedModule.getSourceSection().getSource().getURI()) { + restartFrame = frame; + break; + } + } + if (restartFrame != null) { + // frontend.restartFrame(suspension, restartFrame); + } + //suspension.resum + + System.out.println(suspension.getEvent().getTopStackFrame().getName()); + + +// RootNode node = suspension.getStackFrames().get(2).getRawNode(SomLanguage.class).getRootNode(); +// +// System.out.println(node.getName()); +// System.out.println(node.getCallTarget().); +// DebugStackFrame frame = suspension.getStackFrames().get(2); +// System.out.println(frame.getName()); +// Frame realFrame = frame.getRawFrame(SomLanguage.class, FrameInstance.FrameAccess.READ_ONLY); +// System.out.println("GOT REAL FRAME"); +// + return true; + } + } +}