diff --git a/src/som/compiler/MethodBuilder.java b/src/som/compiler/MethodBuilder.java index b215388bf..72b3416b7 100644 --- a/src/som/compiler/MethodBuilder.java +++ b/src/som/compiler/MethodBuilder.java @@ -37,7 +37,6 @@ import com.oracle.truffle.api.source.SourceSection; import bd.inlining.ScopeAdaptationVisitor; -import bd.inlining.ScopeBuilder; import bd.inlining.nodes.Inlinable; import som.compiler.MixinBuilder.MixinDefinitionError; import som.compiler.MixinBuilder.MixinDefinitionId; @@ -47,8 +46,8 @@ import som.compiler.Variable.Internal; import som.compiler.Variable.Local; import som.compiler.Variable.MutableLocal; +import som.interpreter.LexicalScope; import som.interpreter.LexicalScope.MethodScope; -import som.interpreter.LexicalScope.MixinScope; import som.interpreter.Method; import som.interpreter.SNodeFactory; import som.interpreter.SomLanguage; @@ -65,12 +64,10 @@ import tools.language.StructuralProbe; -public final class MethodBuilder implements ScopeBuilder { +public final class MethodBuilder extends ScopeBuilder + implements bd.inlining.ScopeBuilder { - /** To get to an indirect outer, use outerBuilder. */ - private final MixinBuilder directOuterMixin; - private final MethodBuilder outerBuilder; - private final boolean blockMethod; + private final boolean blockMethod; private final SomLanguage language; @@ -88,8 +85,7 @@ public final class MethodBuilder implements ScopeBuilder { private final LinkedHashMap arguments = new LinkedHashMap<>(); private final LinkedHashMap locals = new LinkedHashMap<>(); - private Internal frameOnStackVar; - private final MethodScope currentScope; + private Internal frameOnStackVar; private final List embeddedBlockMethods; @@ -97,43 +93,39 @@ public final class MethodBuilder implements ScopeBuilder { private final StructuralProbe structuralProbe; - public MethodBuilder(final MixinBuilder holder, final MixinScope clsScope, - final StructuralProbe probe) { - this(holder, clsScope, null, false, holder.getLanguage(), probe); - } - public MethodBuilder(final boolean withoutContext, final SomLanguage language, final StructuralProbe probe) { - this(null, null, null, false, language, probe); + this(null, null, false, language, probe); assert withoutContext; } - public MethodBuilder(final MethodBuilder outerBuilder) { - this(outerBuilder.directOuterMixin, outerBuilder.getHolderScope(), - outerBuilder, true, outerBuilder.language, outerBuilder.structuralProbe); + public MethodBuilder(final MethodBuilder outer) { + this(outer, outer.getScope(), true, outer.language, outer.structuralProbe); } - private MethodBuilder(final MixinBuilder holder, final MixinScope clsScope, - final MethodBuilder outerBuilder, final boolean isBlockMethod, - final SomLanguage language, final StructuralProbe probe) { - this.directOuterMixin = holder; - this.outerBuilder = outerBuilder; + public MethodBuilder(final MixinBuilder outer, final StructuralProbe probe) { + this(outer, outer.getScope(), false, outer.getLanguage(), probe); + } + + public MethodBuilder(final ScopeBuilder outer, final LexicalScope scope, + final boolean isBlockMethod, final SomLanguage language, final StructuralProbe probe) { + super(outer, scope); this.blockMethod = isBlockMethod; this.language = language; this.structuralProbe = probe; - MethodScope outer = (outerBuilder != null) - ? outerBuilder.getCurrentMethodScope() - : null; - assert Nil.nilObject != null : "Nil.nilObject not yet initialized"; - this.currentScope = new MethodScope(new FrameDescriptor(Nil.nilObject), outer, clsScope); - accessesVariablesOfOuterScope = false; throwsNonLocalReturn = false; needsToCatchNonLocalReturn = false; embeddedBlockMethods = new ArrayList(); } + @Override + protected MethodScope createScope(final LexicalScope scope) { + assert Nil.nilObject != null : "Nil.nilObject not yet initialized"; + return new MethodScope(new FrameDescriptor(Nil.nilObject), scope); + } + public static class MethodDefinitionError extends SemanticDefinitionError { private static final long serialVersionUID = 3901992766649011815L; @@ -155,12 +147,12 @@ public Collection getArguments() { */ public void mergeIntoScope(final MethodScope scope, final SInvokable outer) { for (Variable v : scope.getVariables()) { - Local l = v.splitToMergeIntoOuterScope(currentScope.getFrameDescriptor()); + Local l = v.splitToMergeIntoOuterScope(this.scope.getFrameDescriptor()); if (l != null) { // can happen for instance for the block self, which we omit SSymbol name = l.getQualifiedName(); assert !locals.containsKey(name); locals.put(name, l); - currentScope.addVariable(l); + this.scope.addVariable(l); } } SInvokable[] embeddedBlocks = outer.getEmbeddedBlocks(); @@ -172,7 +164,7 @@ public void mergeIntoScope(final MethodScope scope, final SInvokable outer) { if (embeddedScopes != null) { for (MethodScope e : embeddedScopes) { - currentScope.addEmbeddedScope(e.split(currentScope)); + this.scope.addEmbeddedScope(e.split(this.scope)); } for (SInvokable i : embeddedBlocks) { @@ -182,7 +174,7 @@ public void mergeIntoScope(final MethodScope scope, final SInvokable outer) { boolean removed = embeddedBlockMethods.remove(outer); assert removed; - currentScope.removeMerged(scope); + this.scope.removeMerged(scope); } @Override @@ -205,24 +197,20 @@ public bd.inlining.Variable introduceTempForInlinedVersion( public void addEmbeddedBlockMethod(final SInvokable blockMethod) { embeddedBlockMethods.add(blockMethod); - currentScope.addEmbeddedScope(((Method) blockMethod.getInvokable()).getLexicalScope()); - } - - public MethodScope getCurrentMethodScope() { - return currentScope; - } - - public MixinScope getHolderScope() { - return currentScope.getHolderScope(); + scope.addEmbeddedScope(((Method) blockMethod.getInvokable()).getLexicalScope()); } // Name for the frameOnStack slot, // starting with ! to make it a name that's not possible in Smalltalk private static final SSymbol FRAME_ON_STACK_SLOT_NAME = Symbols.symbolFor("!frameOnStack"); + @Override public Internal getFrameOnStackMarkerVar() { - if (outerBuilder != null) { - return outerBuilder.getFrameOnStackMarkerVar(); + if (outer != null) { + Internal result = outer.getFrameOnStackMarkerVar(); + if (result != null) { + return result; + } } if (frameOnStackVar == null) { @@ -230,9 +218,9 @@ public Internal getFrameOnStackMarkerVar() { frameOnStackVar = new Internal(FRAME_ON_STACK_SLOT_NAME); frameOnStackVar.init( - currentScope.getFrameDescriptor().addFrameSlot( + scope.getFrameDescriptor().addFrameSlot( frameOnStackVar, FrameSlotKind.Object)); - currentScope.addVariable(frameOnStackVar); + scope.addVariable(frameOnStackVar); } return frameOnStackVar; } @@ -254,17 +242,19 @@ public boolean requiresContext() { } private MethodBuilder markOuterContextsToRequireContextAndGetRootContext() { - MethodBuilder ctx = outerBuilder; - while (ctx.outerBuilder != null) { + assert outer != null; + assert outer instanceof MethodBuilder; + MethodBuilder ctx = outer.getMethod(); + while (ctx.outer.getMethod() != null) { ctx.throwsNonLocalReturn = true; - ctx = ctx.outerBuilder; + ctx = ctx.outer.getMethod(); } return ctx; } public boolean needsToCatchNonLocalReturn() { // only the most outer method needs to catch - return needsToCatchNonLocalReturn && outerBuilder == null; + return needsToCatchNonLocalReturn && outer.getMethod() == null; } public SInitializer assembleInitializer(final ExpressionNode body, @@ -276,7 +266,7 @@ public SInitializer assembleInitializer(final ExpressionNode body, public SInitializer splitBodyAndAssembleInitializerAs(final SSymbol signature, final ExpressionNode body, final AccessModifier accessModifier, final SourceSection sourceSection) { - MethodScope splitScope = currentScope.split(); + MethodScope splitScope = scope.split(); ExpressionNode splitBody = ScopeAdaptationVisitor.adapt(body, splitScope, 0, false); Method truffleMeth = assembleInvokable(splitBody, splitScope, sourceSection); @@ -326,22 +316,22 @@ public void setVarsOnMethodScope() { vars[i] = l; i += 1; } - currentScope.setVariables(vars); + scope.setVariables(vars); } public void finalizeMethodScope() { - currentScope.finalizeScope(); + scope.finalizeScope(); } public Method assembleInvokable(final ExpressionNode body, final SourceSection sourceSection) { - return assembleInvokable(body, currentScope, sourceSection); + return assembleInvokable(body, scope, sourceSection); } private String getMethodIdentifier() { - MixinBuilder holder = getEnclosingMixinBuilder(); + MixinBuilder holder = getMixin(); String cls = holder != null && holder.isClassSide() ? "_class" : ""; - String name = holder == null ? "_unknown_" : holder.getName().getString(); + String name = holder == null ? "_unknown_" : holder.getName(); return name + cls + ">>" + signature.toString(); } @@ -383,8 +373,8 @@ public void addArgument(final SSymbol arg, final SourceSection source) { public Local addMessageCascadeTemp(final SourceSection source) throws MethodDefinitionError { cascadeId += 1; Local l = addLocal(Symbols.symbolFor("$cascadeTmp" + cascadeId), true, source); - if (currentScope.hasVariables()) { - currentScope.addVariable(l); + if (scope.hasVariables()) { + scope.addVariable(l); } return l; } @@ -402,7 +392,7 @@ public Local addLocal(final SSymbol name, final boolean immutable, } else { l = new MutableLocal(name, source); } - l.init(currentScope.getFrameDescriptor().addFrameSlot(l)); + l.init(scope.getFrameDescriptor().addFrameSlot(l)); locals.put(name, l); if (structuralProbe != null) { @@ -414,7 +404,7 @@ public Local addLocal(final SSymbol name, final boolean immutable, private Local addLocalAndUpdateScope(final SSymbol name, final boolean immutable, final SourceSection source) throws MethodDefinitionError { Local l = addLocal(name, immutable, source); - currentScope.addVariable(l); + scope.addVariable(l); return l; } @@ -424,9 +414,9 @@ public boolean isBlockMethod() { private int getOuterSelfContextLevel() { int level = 0; - MethodBuilder ctx = outerBuilder; + MethodBuilder ctx = outer.getMethod(); while (ctx != null) { - ctx = ctx.outerBuilder; + ctx = ctx.outer.getMethod(); level++; } return level; @@ -455,21 +445,16 @@ private int getOuterSelfContextLevel() { * variable cannot be in scope and that a variable must have been erroneously * by {@link #getReadNode} or {@link #getWriteNode}. */ - private int getContextLevel(final SSymbol varName) { + @Override + protected int getContextLevel(final SSymbol varName) { // Check the current activation if (locals.containsKey(varName) || arguments.containsKey(varName)) { return 0; } - // Check all enclosing activations (ends after first method activation) - if (outerBuilder != null) { - return 1 + outerBuilder.getContextLevel(varName); - } - - // Otherwise, the method belongs to an object literal, check the object's - // enclosing activations - assert directOuterMixin != null && directOuterMixin.isLiteral(); - return 1 + directOuterMixin.getEnclosingMethod().getContextLevel(varName); + // Check all enclosing activations + assert outer != null; + return 1 + outer.getContextLevel(varName); } public Local getEmbeddedLocal(final String embeddedName) { @@ -484,8 +469,8 @@ public Local getEmbeddedLocal(final String embeddedName) { * Refer to {@link #getContextLevel} for a description of how the * enclosing scopes are traversed. */ + @Override protected Variable getVariable(final SSymbol varName) { - // Check the current activation if (locals.containsKey(varName)) { return locals.get(varName); @@ -495,9 +480,9 @@ protected Variable getVariable(final SSymbol varName) { return arguments.get(varName); } - // Check all enclosing activations (ends after first method activation) - if (outerBuilder != null) { - Variable outerVar = outerBuilder.getVariable(varName); + // Check all enclosing activations + if (outer != null) { + Variable outerVar = outer.getVariable(varName); if (outerVar != null) { accessesVariablesOfOuterScope = true; if (outerVar instanceof Local) { @@ -507,19 +492,17 @@ protected Variable getVariable(final SSymbol varName) { } } - // If the method belongs to an object literal, check the object's enclosing - // activations - if (directOuterMixin != null && directOuterMixin.isLiteral()) { - Variable literalVar = directOuterMixin.getEnclosingMethod().getVariable(varName); - if (literalVar != null) { - accessesVariablesOfOuterScope = true; - if (literalVar instanceof Local) { - accessesLocalOfOuterScope = true; - } - return literalVar; + return null; + } + + @Override + protected boolean isImmutable() { + for (Local l : locals.values()) { + if (l.isMutable()) { + return false; } } - return null; + return outer.isImmutable(); } public Argument getSelf() { @@ -528,14 +511,14 @@ public Argument getSelf() { public ExpressionNode getSuperReadNode(final SourceSection source) { assert source != null; - MixinBuilder holder = getEnclosingMixinBuilder(); + MixinBuilder holder = getMixin(); return getSelf().getSuperReadNode(getOuterSelfContextLevel(), new AccessNodeState(holder.getMixinId(), holder.isClassSide()), source); } public ExpressionNode getSelfRead(final SourceSection source) { assert source != null; - MixinBuilder holder = getEnclosingMixinBuilder(); + MixinBuilder holder = getMixin(); MixinDefinitionId mixinId = holder == null ? null : holder.getMixinId(); return getSelf().getThisReadNode(getContextLevel(Symbols.SELF), new AccessNodeState(mixinId), source); @@ -572,8 +555,8 @@ public ExpressionNode getImplicitReceiverSend(final SSymbol selector, // send the message to self if at top level (classes only) or // if self has the slot defined (object literals only) - if (getEnclosingMixinBuilder() == null || - getEnclosingMixinBuilder().hasSlotDefined(selector)) { + if (getMixin() == null || + getMixin().hasSlotDefined(selector)) { return SNodeFactory.createMessageSend(selector, new ExpressionNode[] {getSelfRead(source)}, false, source, null, language); } @@ -587,7 +570,7 @@ public ExpressionNode getImplicitReceiverSend(final SSymbol selector, // otherwise it's an implicit send return SNodeFactory.createImplicitReceiverSend(selector, new ExpressionNode[] {getSelfRead(source)}, - getCurrentMethodScope(), getEnclosingMixinBuilder().getMixinId(), + scope, getMixin().getMixinId(), source, language.getVM()); } @@ -599,6 +582,9 @@ public ExpressionNode getSetterSend(final SSymbol setter, String varNameStr = setterName.substring(0, setterName.length() - 1); SSymbol varName = Symbols.symbolFor(varNameStr); + // TODO: this looks strange. can an inner name shadow any outer name, I would think so + // so, this seems incorrect to check first the whole chain for the name instead of doing it + // only step wise if (hasArgument(varName)) { throw new MethodDefinitionError("Can't assign to argument: " + varName, source); } @@ -611,12 +597,12 @@ public ExpressionNode getSetterSend(final SSymbol setter, // send the message to self if at top level (classes only) or // if self has the slot defined (object literals only) - if (getEnclosingMixinBuilder() == null || - getEnclosingMixinBuilder().hasSlotDefined(setter)) { + if (getMixin() == null || + getMixin().hasSlotDefined(setter)) { return SNodeFactory.createImplicitReceiverSend( MixinBuilder.getSetterName(setter), new ExpressionNode[] {getSelfRead(source), exp}, - getCurrentMethodScope(), getEnclosingMixinBuilder().getMixinId(), + scope, getMixin().getMixinId(), source, language.getVM()); } @@ -630,17 +616,18 @@ public ExpressionNode getSetterSend(final SSymbol setter, return SNodeFactory.createImplicitReceiverSend( Symbols.symbolFor(setterName), new ExpressionNode[] {getSelfRead(source), exp}, - getCurrentMethodScope(), getEnclosingMixinBuilder().getMixinId(), + scope, getMixin().getMixinId(), source, language.getVM()); } + @Override protected boolean hasArgument(final SSymbol varName) { if (arguments.containsKey(varName)) { return true; } - if (outerBuilder != null) { - return outerBuilder.hasArgument(varName); + if (outer != null) { + return outer.hasArgument(varName); } return false; } @@ -649,34 +636,23 @@ protected boolean hasArgument(final SSymbol varName) { * Refer to {@link #getContextLevel} for a description of how the * enclosing scopes are traversed. */ + @Override protected Local getLocal(final SSymbol varName) { // Check the current activation if (locals.containsKey(varName)) { return locals.get(varName); } - // Check all enclosing activations (ends after first method activation) - if (outerBuilder != null) { - Local outerLocal = outerBuilder.getLocal(varName); + // Check all enclosing activations + if (outer != null) { + Local outerLocal = outer.getLocal(varName); if (outerLocal != null) { accessesVariablesOfOuterScope = true; accessesLocalOfOuterScope = true; return outerLocal; } - } - // If the method belongs to an object literal, check the object's enclosing - // activations - if (directOuterMixin != null && directOuterMixin.isLiteral()) { - Local literalLocal = directOuterMixin.getEnclosingMethod().getLocal(varName); - if (literalLocal != null) { - accessesVariablesOfOuterScope = true; - accessesLocalOfOuterScope = true; - return literalLocal; - } - - } return null; } @@ -686,30 +662,33 @@ public ReturnNonLocalNode getNonLocalReturn(final ExpressionNode expr) { getOuterSelfContextLevel()); } - public MethodBuilder getOuterBuilder() { - return outerBuilder; + @Override + public String getName() { + return signature.getString(); } - public MixinBuilder getEnclosingMixinBuilder() { - if (this.directOuterMixin == null) { - if (outerBuilder == null) { - return null; - } else { - return outerBuilder.getEnclosingMixinBuilder(); - } + @Override + public MixinBuilder getMixin() { + if (outer != null) { + return outer.getMixin(); } else { - return directOuterMixin; + return null; } } + @Override + public MethodBuilder getMethod() { + return this; + } + public ExpressionNode getOuterRead(final String outerName, final SourceSection source) throws MixinDefinitionError { - MixinBuilder enclosing = getEnclosingMixinBuilder(); + MixinBuilder enclosing = getMixin(); MixinDefinitionId lexicalSelfMixinId = enclosing.getMixinId(); int ctxLevel = 0; - while (!outerName.equals(enclosing.getName().getString())) { + while (!outerName.equals(enclosing.getName())) { ctxLevel++; - enclosing = enclosing.getOuterBuilder(); + enclosing = enclosing.getOuter().getMixin(); if (enclosing == null) { throw new MixinDefinitionError("Outer send `outer " + outerName + "` could not be resolved", source); @@ -742,7 +721,8 @@ public void addMethodDefinitionSource(final SourceSection source) { @Override public String toString() { - return "MethodBuilder(" + getEnclosingMixinBuilder().getName().getString() + - ">>" + signature.toString() + ")"; + MixinBuilder mixin = getMixin(); + String name = mixin == null ? "" : mixin.getName(); + return "MethodBuilder(" + name + ">>" + signature.toString() + ")"; } } diff --git a/src/som/compiler/MixinBuilder.java b/src/som/compiler/MixinBuilder.java index 2d3993029..b9db5ffb7 100644 --- a/src/som/compiler/MixinBuilder.java +++ b/src/som/compiler/MixinBuilder.java @@ -38,7 +38,9 @@ import som.compiler.MixinDefinition.SlotDefinition; import som.compiler.MixinDefinition.SlotMutator; import som.compiler.Variable.Argument; -import som.interpreter.LexicalScope.MethodScope; +import som.compiler.Variable.Internal; +import som.compiler.Variable.Local; +import som.interpreter.LexicalScope; import som.interpreter.LexicalScope.MixinScope; import som.interpreter.Method; import som.interpreter.SNodeFactory; @@ -58,7 +60,7 @@ * MixinBuilders are used by the parser to accumulate all information to create * a {@link MixinDefinition}. */ -public final class MixinBuilder { +public final class MixinBuilder extends ScopeBuilder { // TODO: if performance critical, optimize mixin builder by initializing structures lazily /** The method that is used to resolve the superclass at runtime. */ @@ -103,12 +105,8 @@ public final class MixinBuilder { private final AccessModifier accessModifier; - private final MixinScope instanceScope; private final MixinScope classScope; - private final MixinBuilder outerMixin; - private final MethodBuilder outerMethod; - private final MixinDefinitionId mixinId; private final StructuralProbe structuralProbe; @@ -138,66 +136,28 @@ public String toString() { } }; - private MixinBuilder(final MixinBuilder outerMixin, final MethodBuilder outerMethod, - final AccessModifier accessModifier, final SSymbol name, - final SourceSection nameSection, - final StructuralProbe structuralProbe, - final SomLanguage language) { + public MixinBuilder(final ScopeBuilder outer, final AccessModifier accessModifier, + final SSymbol name, final SourceSection nameSection, + final StructuralProbe structuralProbe, final SomLanguage language) { + super(outer, outer == null ? null : outer.getScope()); this.name = name; this.nameSection = nameSection; this.mixinId = new MixinDefinitionId(name); this.classSide = false; - this.outerMixin = outerMixin; - this.outerMethod = outerMethod; this.language = language; - // classes can only be defined on the instance side, - // so, both time the instance scope - MethodScope outerMethodScope = - outerMethod != null ? outerMethod.getCurrentMethodScope() : null; - MixinScope outerMixinScope = outerMixin != null ? outerMixin.getInstanceScope() : null; - this.instanceScope = new MixinScope(outerMixinScope, outerMethodScope); - this.classScope = new MixinScope(outerMixinScope, outerMethodScope); + this.classScope = createScope(scope); - this.initializer = new MethodBuilder(this, this.instanceScope, structuralProbe); - this.primaryFactoryMethod = new MethodBuilder(this, this.classScope, structuralProbe); + this.initializer = new MethodBuilder(this, structuralProbe); + this.primaryFactoryMethod = + new MethodBuilder(this, classScope, false, language, structuralProbe); this.superclassAndMixinResolutionBuilder = createSuperclassResolutionBuilder(); this.accessModifier = accessModifier; this.structuralProbe = structuralProbe; } - /** - * This constructor is used to create a MixinBuilder for a Newspeak classes, - * which must either occur at the top lexical level or be nested inside of - * another class declaration (the outerMixin). - */ - public MixinBuilder(final MixinBuilder outerMixin, - final AccessModifier accessModifier, final SSymbol name, - final SourceSection nameSection, - final StructuralProbe structuralProbe, - final SomLanguage language) { - this(outerMixin, null, accessModifier, name, nameSection, - structuralProbe, language); - } - - /** - * This constructor is used to create a MixinBuilder for an object literal, - * which must be inside of an activation (the outerMethod). - * Since the object literal inherits from a class, we still need to hold - * a reference to the outer class declaration - the mixin that - * encloses the current activation. - */ - public MixinBuilder(final MethodBuilder outerMethod, - final AccessModifier accessModifier, final SSymbol name, - final SourceSection nameSection, - final StructuralProbe structuralProbe, - final SomLanguage language) { - this(outerMethod.getEnclosingMixinBuilder(), outerMethod, - accessModifier, name, nameSection, structuralProbe, language); - } - public static class MixinDefinitionError extends SemanticDefinitionError { private static final long serialVersionUID = 5030639383869198851L; @@ -206,38 +166,82 @@ public static class MixinDefinitionError extends SemanticDefinitionError { } } - public SomLanguage getLanguage() { - return language; + @Override + protected MixinScope createScope(final LexicalScope outer) { + return new MixinScope(outer); } - public MixinScope getInstanceScope() { - return instanceScope; + @Override + public MixinBuilder getMixin() { + return this; } - public MixinBuilder getOuterBuilder() { - return outerMixin; + @Override + public MethodBuilder getMethod() { + return null; } - public boolean isLiteral() { - return outerMethod != null; + public SomLanguage getLanguage() { + return language; } - public MethodBuilder getEnclosingMethod() { - return outerMethod; + public boolean isLiteral() { + return outer instanceof MethodBuilder; } - public SSymbol getName() { - return name; + @Override + public String getName() { + return name.getString(); } public boolean isModule() { - return outerMixin == null; + return outer == null; } public AccessModifier getAccessModifier() { return accessModifier; } + @Override + protected int getContextLevel(final SSymbol varName) { + assert outer != null : "If there is no outer context, " + + "something is wrong with lexcial scoping. Could not find var: " + + varName.getString(); + return outer.getContextLevel(varName); + } + + @Override + protected Local getLocal(final SSymbol varName) { + if (outer == null) { + return null; + } + return outer.getLocal(varName); + } + + @Override + protected Variable getVariable(final SSymbol varName) { + if (outer == null) { + return null; + } + return outer.getVariable(varName); + } + + @Override + protected boolean hasArgument(final SSymbol varName) { + if (outer == null) { + return false; + } + return outer.hasArgument(varName); + } + + @Override + public Internal getFrameOnStackMarkerVar() { + // null, because we use this for non-local returns, + // which are returning from methods + // so, with this method, we just look for the closest enclosing object method + return null; + } + /** * Expression to resolve the super class at runtime, used in the instantiation. */ @@ -409,7 +413,7 @@ public MixinScope getScopeForCurrentParserPosition() { if (classSide) { return classScope; } else { - return instanceScope; + return scope; } } @@ -442,9 +446,9 @@ public MixinDefinition assemble(final SourceSection source) { primaryFactory.getSignature(), slotAndInitExprs, initializer, initializerSource, superclassResolution, slots, dispatchables, factoryMethods, embeddedMixins, mixinId, - accessModifier, instanceScope, classScope, allSlotsAreImmutable, - outerScopeIsImmutable(), isModule(), source); - instanceScope.setMixinDefinition(clsDef, false); + accessModifier, scope, classScope, allSlotsAreImmutable, + isModule() || outer.isImmutable(), isModule(), source); + scope.setMixinDefinition(clsDef, false); classScope.setMixinDefinition(clsDef, true); setHolders(clsDef); @@ -455,11 +459,17 @@ public MixinDefinition assemble(final SourceSection source) { return clsDef; } - private boolean outerScopeIsImmutable() { - if (isModule()) { - return true; + @Override + protected boolean isImmutable() { + if (!allSlotsAreImmutable) { + return false; } - return outerMixin.allSlotsAreImmutable && outerMixin.outerScopeIsImmutable(); + + if (outer != null) { + return outer.isImmutable(); + } + + return true; } private void setHolders(final MixinDefinition clsDef) { @@ -480,8 +490,8 @@ private MethodBuilder createSuperclassResolutionBuilder() { if (isModule()) { definitionMethod = new MethodBuilder(true, language, structuralProbe); } else { - definitionMethod = new MethodBuilder(outerMixin, - outerMixin.getInstanceScope(), structuralProbe); + definitionMethod = + new MethodBuilder(outer, outer.scope, false, language, structuralProbe); } // self is going to be the enclosing object diff --git a/src/som/compiler/MixinDefinition.java b/src/som/compiler/MixinDefinition.java index f315d5124..906baf06b 100644 --- a/src/som/compiler/MixinDefinition.java +++ b/src/som/compiler/MixinDefinition.java @@ -796,9 +796,8 @@ private void adaptInvokableDispatchables(final MethodScope scope, final int appl public MixinDefinition cloneAndAdaptAfterScopeChange(final MethodScope adaptedScope, final int appliesTo) { - MixinScope adaptedInstanceScope = - new MixinScope(instanceScope.getOuterMixin(), adaptedScope); - MixinScope adaptedClassScope = new MixinScope(classScope.getOuterMixin(), adaptedScope); + MixinScope adaptedInstanceScope = new MixinScope(adaptedScope); + MixinScope adaptedClassScope = new MixinScope(adaptedScope); MixinDefinition clone = cloneForSplitting(adaptedInstanceScope, adaptedClassScope); diff --git a/src/som/compiler/Parser.java b/src/som/compiler/Parser.java index 4ec3242e3..c506832a8 100644 --- a/src/som/compiler/Parser.java +++ b/src/som/compiler/Parser.java @@ -829,8 +829,7 @@ public SourceSection getSource(final SourceCoordinate coord) { private void methodDeclaration(final AccessModifier accessModifier, final SourceCoordinate coord, final MixinBuilder mxnBuilder) throws ProgramDefinitionError { - MethodBuilder builder = new MethodBuilder( - mxnBuilder, mxnBuilder.getScopeForCurrentParserPosition(), structuralProbe); + MethodBuilder builder = new MethodBuilder(mxnBuilder, structuralProbe); comments(); @@ -1441,8 +1440,8 @@ protected ExpressionNode keywordMessage(final MethodBuilder builder, } else { assert !eventualSend; return createImplicitReceiverSend(msg, args, - builder.getCurrentMethodScope(), - builder.getEnclosingMixinBuilder().getMixinId(), source, language.getVM()); + builder.getScope(), + builder.getMixin().getMixinId(), source, language.getVM()); } } @@ -1690,7 +1689,7 @@ private ExpressionNode nestedBlock(final MethodBuilder builder) blockPattern(builder); } - String outerMethodName = stripColons(builder.getOuterBuilder().getSignature().getString()); + String outerMethodName = stripColons(builder.getOuter().getName()); // generate Block signature String blockSig = "λ" + outerMethodName + "@" + coord.startLine + "@" + coord.startColumn; diff --git a/src/som/compiler/ScopeBuilder.java b/src/som/compiler/ScopeBuilder.java new file mode 100644 index 000000000..10a14984d --- /dev/null +++ b/src/som/compiler/ScopeBuilder.java @@ -0,0 +1,50 @@ +package som.compiler; + +import som.compiler.Variable.Internal; +import som.compiler.Variable.Local; +import som.interpreter.LexicalScope; +import som.vmobjects.SSymbol; + + +/** + * The super class of {@link MixinBuilder} and {@link MethodBuilder}. + * It manages scope information; + */ +public abstract class ScopeBuilder { + protected final ScopeBuilder outer; + + protected final LS scope; + + protected ScopeBuilder(final ScopeBuilder outer, final LexicalScope outerScope) { + this.outer = outer; + this.scope = createScope(outerScope); + } + + public final ScopeBuilder getOuter() { + return outer; + } + + public LS getScope() { + return scope; + } + + protected abstract LS createScope(LexicalScope outer); + + public abstract MixinBuilder getMixin(); + + public abstract MethodBuilder getMethod(); + + protected abstract int getContextLevel(SSymbol varName); + + protected abstract Local getLocal(SSymbol varName); + + protected abstract Variable getVariable(SSymbol varName); + + protected abstract boolean hasArgument(SSymbol varName); + + public abstract Internal getFrameOnStackMarkerVar(); + + protected abstract boolean isImmutable(); + + public abstract String getName(); +} diff --git a/src/som/compiler/Variable.java b/src/som/compiler/Variable.java index 825c494a3..9586fc6bb 100644 --- a/src/som/compiler/Variable.java +++ b/src/som/compiler/Variable.java @@ -209,6 +209,8 @@ public FrameSlot getSlot() { protected abstract Local create(); + public abstract boolean isMutable(); + @Override public Local split(final FrameDescriptor descriptor) { Local newLocal = create(); @@ -252,6 +254,11 @@ public static final class MutableLocal extends Local { public Local create() { return new MutableLocal(name, source); } + + @Override + public boolean isMutable() { + return true; + } } public static final class ImmutableLocal extends Local { @@ -263,6 +270,11 @@ public static final class ImmutableLocal extends Local { public Local create() { return new ImmutableLocal(name, source); } + + @Override + public boolean isMutable() { + return false; + } } /** diff --git a/src/som/interpreter/LexicalScope.java b/src/som/interpreter/LexicalScope.java index ce4733348..9d8d60afe 100644 --- a/src/som/interpreter/LexicalScope.java +++ b/src/som/interpreter/LexicalScope.java @@ -19,12 +19,29 @@ public abstract class LexicalScope { + protected final LexicalScope outerScope; + + protected LexicalScope(final LexicalScope outerScope) { + this.outerScope = outerScope; + } + + public abstract MixinScope getMixinScope(); + + public abstract MethodScope getMethodScope(); + + public abstract MethodScope getOuterMethod(); + + public abstract MixinScope getOuterMixin(); + + public abstract MixinIdAndContextLevel lookupSlotOrClass(SSymbol selector, + int bbjectContextLevel); + + // public abstract void propagateLoopCountThroughoutMethodScope(long count); + // TODO: figure out whether we can use this lexical scope also for the // super sends. seems like we currently have two similar ways to solve // similar problems, instead of a single one public static final class MixinScope extends LexicalScope { - private final MixinScope outerMixin; - private final MethodScope nullOrOuterMethod; private HashMap slotsClassesAndMethods; @CompilationFinal private MixinDefinition mixinDefinition; @@ -32,25 +49,39 @@ public static final class MixinScope extends LexicalScope { /** * Both Newspeak's class and the class's created anonymously for object literal's * need to be instantiated from the outer class. Regular classes do not have - * an enclosing activation, and so for regular classes the parameter corresponding to - * the outer method is null. In contrast, the class of an object literal has the outer - * method as its enclosing activation, and in this case the scope of the outer method - * is given. + * an enclosing activation. In that case the {@code outerScope} is a {@link MixinScope}. + * For object literals, it is a {@link MethodScope}. * - * @param outerMixin, the scope of the outer class - * @param nullOrOuterMethod, either null or the scope of the current activation + * @param outerScope, the scope enclosing the element */ - public MixinScope(final MixinScope outerMixin, final MethodScope nullOrOuterMethod) { - this.outerMixin = outerMixin; - this.nullOrOuterMethod = nullOrOuterMethod; + public MixinScope(final LexicalScope outerScope) { + super(outerScope); } - public MethodScope getOuterMethod() { - return nullOrOuterMethod; + @Override + public MixinScope getMixinScope() { + return this; } + @Override public MixinScope getOuterMixin() { - return outerMixin; + if (outerScope == null) { + return null; + } + return outerScope.getMixinScope(); + } + + @Override + public MethodScope getOuterMethod() { + if (outerScope == null) { + return null; + } + return outerScope.getMethodScope(); + } + + @Override + public MethodScope getMethodScope() { + return outerScope.getMethodScope(); } public HashMap getDispatchables() { @@ -85,15 +116,16 @@ public String toString() { } } + @Override public MixinIdAndContextLevel lookupSlotOrClass(final SSymbol selector, - final int contextLevel) { + final int objectContextLevel) { assert mixinDefinition != null; if (slotsClassesAndMethods.containsKey(selector)) { - return new MixinIdAndContextLevel(mixinDefinition.getMixinId(), contextLevel); + return new MixinIdAndContextLevel(mixinDefinition.getMixinId(), objectContextLevel); } - if (outerMixin != null) { - return outerMixin.lookupSlotOrClass(selector, contextLevel + 1); + if (outerScope != null) { + return outerScope.lookupSlotOrClass(selector, objectContextLevel + 1); } return null; } @@ -118,9 +150,6 @@ public MixinDefinition getMixinDefinition() { */ public static final class MethodScope extends LexicalScope implements Scope { - private final MethodScope outerMethod; - private final MixinScope outerMixin; - private final FrameDescriptor frameDescriptor; @CompilationFinal(dimensions = 1) private MethodScope[] embeddedScopes; @@ -134,11 +163,9 @@ public static final class MethodScope extends LexicalScope @CompilationFinal private Method method; - public MethodScope(final FrameDescriptor frameDescriptor, - final MethodScope outerMethod, final MixinScope outerMixin) { + public MethodScope(final FrameDescriptor frameDescriptor, final LexicalScope outerScope) { + super(outerScope); this.frameDescriptor = frameDescriptor; - this.outerMethod = outerMethod; - this.outerMixin = outerMixin; } public void setVariables(final Variable[] variables) { @@ -163,7 +190,7 @@ public void addVariable(final Variable var) { } public void addEmbeddedScope(final MethodScope embeddedScope) { - assert embeddedScope.outerMethod == this; + assert embeddedScope.outerScope == this; int length; if (embeddedScopes == null) { length = 0; @@ -193,6 +220,11 @@ public void removeMerged(final MethodScope scope) { embeddedScopes = remainingScopes; } + @Override + public MethodScope getOuterScopeOrNull() { + return outerScope.getMethodScope(); + } + @Override public MethodScope getScope(final Method method) { if (method.equals(this.method)) { @@ -232,7 +264,7 @@ public MethodScope[] getEmbeddedScopes() { return embeddedScopes; } - private MethodScope constructSplitScope(final MethodScope newOuter) { + private MethodScope constructSplitScope(final LexicalScope newOuter) { FrameDescriptor desc = new FrameDescriptor(frameDescriptor.getDefaultValue()); Variable[] newVars = new Variable[variables.length]; @@ -240,7 +272,7 @@ private MethodScope constructSplitScope(final MethodScope newOuter) { newVars[i] = variables[i].split(desc); } - MethodScope split = new MethodScope(desc, newOuter, outerMixin); + MethodScope split = new MethodScope(desc, newOuter); if (embeddedScopes != null) { for (MethodScope s : embeddedScopes) { @@ -257,7 +289,7 @@ private MethodScope constructSplitScope(final MethodScope newOuter) { /** Split lexical scope. */ public MethodScope split() { assert isFinalized(); - return constructSplitScope(outerMethod); + return constructSplitScope(outerScope); } /** @@ -265,7 +297,7 @@ public MethodScope split() { * One of the outer scopes was inlined into its parent, * or simply split itself. */ - public MethodScope split(final MethodScope newOuter) { + public MethodScope split(final LexicalScope newOuter) { assert isFinalized(); return constructSplitScope(newOuter); } @@ -274,22 +306,9 @@ public FrameDescriptor getFrameDescriptor() { return frameDescriptor; } - @Override - public MethodScope getOuterScopeOrNull() { - if (outerMethod != null) { - return outerMethod; - } - - if (outerMixin.getOuterMethod() != null) { - return outerMixin.getOuterMethod(); - } - - return null; - } - public void propagateLoopCountThroughoutMethodScope(final long count) { - if (outerMethod != null) { - outerMethod.method.propagateLoopCountThroughoutMethodScope(count); + if (outerScope != null) { + outerScope.getMethodScope().method.propagateLoopCountThroughoutMethodScope(count); } } @@ -324,19 +343,36 @@ public String toString() { } public MixinIdAndContextLevel lookupSlotOrClass(final SSymbol selector) { - return getEnclosingMixin().lookupSlotOrClass(selector, 0); + return getMixinScope().lookupSlotOrClass(selector, 0); } - public MixinScope getEnclosingMixin() { - if (outerMethod == null) { - return outerMixin; - } else { - return outerMethod.getEnclosingMixin(); - } + @Override + public MixinScope getMixinScope() { + assert outerScope != null : "Should not be possible, because we do not support top-level methods"; + return outerScope.getMixinScope(); } - public MixinScope getHolderScope() { - return outerMixin; + @Override + public MixinScope getOuterMixin() { + return getMixinScope(); + } + + @Override + public MethodScope getMethodScope() { + return this; + } + + @Override + public MethodScope getOuterMethod() { + return outerScope.getMethodScope(); + } + + @Override + public MixinIdAndContextLevel lookupSlotOrClass(final SSymbol selector, + final int objectContextLevel) { + assert outerScope != null : "Should not be possible, because we do not support top-level methods"; + // REM: We don't count methods, because we only need the number of enclosing objects + return outerScope.lookupSlotOrClass(selector, objectContextLevel); } public MethodScope getEmbeddedScope(final SourceSection source) { diff --git a/src/som/interpreter/Method.java b/src/som/interpreter/Method.java index 74144fa94..12d38e0ab 100644 --- a/src/som/interpreter/Method.java +++ b/src/som/interpreter/Method.java @@ -90,7 +90,7 @@ public String toString() { public ExpressionNode inline(final MethodBuilder builder, final SInvokable outer) { builder.mergeIntoScope(methodScope, outer); return ScopeAdaptationVisitor.adapt( - uninitializedBody, builder.getCurrentMethodScope(), 0, true); + uninitializedBody, builder.getScope(), 0, true); } @Override diff --git a/src/som/interpreter/nodes/ExpressionNode.java b/src/som/interpreter/nodes/ExpressionNode.java index 0cbcd9c55..2964a6d76 100644 --- a/src/som/interpreter/nodes/ExpressionNode.java +++ b/src/som/interpreter/nodes/ExpressionNode.java @@ -157,4 +157,9 @@ public boolean isResultUsed(final ExpressionNode child) { } return true; } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + sourceSection.toString() + ")"; + } } diff --git a/src/som/vm/ObjectSystem.java b/src/som/vm/ObjectSystem.java index dcf67f9f6..ec9b38174 100644 --- a/src/som/vm/ObjectSystem.java +++ b/src/som/vm/ObjectSystem.java @@ -135,7 +135,7 @@ public MixinDefinition loadModule(final Source source) throws IOException { private SObjectWithoutFields constructVmMirror() { HashMap vmMirrorMethods = primitives.takeVmMirrorPrimitives(); - MixinScope scope = new MixinScope(null, null); + MixinScope scope = new MixinScope(null); MixinDefinition vmMirrorDef = new MixinDefinition( Symbols.VMMIRROR, null, null, null, null, null, null, null, diff --git a/src/som/vm/Primitives.java b/src/som/vm/Primitives.java index 18ca64eec..a0112b2ef 100644 --- a/src/som/vm/Primitives.java +++ b/src/som/vm/Primitives.java @@ -142,7 +142,7 @@ private static SInvokable constructVmMirrorPrimitive(final SSymbol signature, String name = "vmMirror>>" + signature.toString(); Primitive primMethodNode = new Primitive(name, primNode, - prim.getCurrentMethodScope().getFrameDescriptor(), + prim.getScope().getFrameDescriptor(), (ExpressionNode) primNode.deepCopy(), false, lang); return new SInvokable(signature, AccessModifier.PUBLIC, primMethodNode, null);