diff --git a/runtime/src/main/java/dev/ionfusion/fusion/LambdaForm.java b/runtime/src/main/java/dev/ionfusion/fusion/LambdaForm.java index b54b0524..6f2690fc 100644 --- a/runtime/src/main/java/dev/ionfusion/fusion/LambdaForm.java +++ b/runtime/src/main/java/dev/ionfusion/fusion/LambdaForm.java @@ -41,14 +41,11 @@ SyntaxValue> expand(Expander expander, Environment env, SyntaxSexp stx) var formals = children[1]; var args = flattenFormals(eval, check, formals); - // When there's no args, we can avoid an empty binding rib at runtime. - // TODO #67 This will need changing for internal definitions - // since we won't know yet whether the rib will be empty or not. LocalEnvironment localEnv = null; if (args.length != 0) { ensureUniqueIdentifiers(eval, args, stx); - env = localEnv = new LocalEnvironment(env, args); + env = localEnv = LocalEnvironment.forExpansion(env, args); } // Prepare the bound names so they resolve to their own binding. @@ -149,11 +146,8 @@ CompiledForm compile(Compiler comp, Environment env, SyntaxSexp stx) var formalsDecl = stx.get(eval, 1); var formals = flattenFormals(eval, formalsDecl); - if (formals.length != 0) - { - // We micro-optimize by avoiding an environment frame for no-arg procedures. - env = LocalEnvironment.forBinders(env, formals); - } + + env = LocalEnvironment.forCompilation(env, formals); CompiledForm body = comp.compileBegin(env, stx, 2); @@ -414,8 +408,7 @@ Object doApply(Evaluator eval, Object[] args) { checkArityExact(eval, myArity, args); - Store localStore = new LocalStore(myEnclosure, args); - + Store localStore = LocalStore.forExecution(myEnclosure, args); return eval.bounceTailForm(localStore, myBody); } } diff --git a/runtime/src/main/java/dev/ionfusion/fusion/LetValuesForm.java b/runtime/src/main/java/dev/ionfusion/fusion/LetValuesForm.java index da54161a..8977cd32 100644 --- a/runtime/src/main/java/dev/ionfusion/fusion/LetValuesForm.java +++ b/runtime/src/main/java/dev/ionfusion/fusion/LetValuesForm.java @@ -68,7 +68,7 @@ SyntaxValue expand(Expander expander, Environment env, SyntaxSexp stx) { boundNames = boundNameList.toArray(SyntaxSymbol.EMPTY_ARRAY); ensureUniqueIdentifiers(eval, boundNames, stx); - var localEnv = new LocalEnvironment(env, boundNames); + var localEnv = LocalEnvironment.forExpansion(env, boundNames); localWrap = localEnv.getWrap(); bodyEnv = localEnv; } @@ -171,11 +171,7 @@ CompiledForm compile(Compiler comp, Environment env, SyntaxSexp expr) valuePosns[i] = boundExpr.getPosition(); } - if (bindingCount != 0) - { - // Dummy environment to keep track of depth - env = LocalEnvironment.forBinders(env, binders.toArray(new SyntaxSymbol[0])); - } + env = LocalEnvironment.forCompilation(env, binders.toArray(new SyntaxSymbol[0])); CompiledForm body = comp.compileBegin(env, expr, 2); @@ -249,7 +245,7 @@ public Object doEval(Evaluator eval, Store store) boundValues[i] = values; } - Store localStore = new LocalStore(store, boundValues); + Store localStore = LocalStore.forExecution(store, boundValues); return eval.bounceTailForm(localStore, myBody); } } @@ -393,7 +389,7 @@ else if (values instanceof Object[]) } } - Store localStore = LocalStore.forLocals(store, boundValues); + Store localStore = LocalStore.forExecution(store, boundValues); return eval.bounceTailForm(localStore, myBody); } } diff --git a/runtime/src/main/java/dev/ionfusion/fusion/LetrecForm.java b/runtime/src/main/java/dev/ionfusion/fusion/LetrecForm.java index 4ddf387d..906cb4e0 100644 --- a/runtime/src/main/java/dev/ionfusion/fusion/LetrecForm.java +++ b/runtime/src/main/java/dev/ionfusion/fusion/LetrecForm.java @@ -37,7 +37,7 @@ SyntaxValue expand(Expander expander, Environment env, SyntaxSexp stx) } ensureUniqueIdentifiers(eval, boundNames, stx); - var bodyEnv = new LocalEnvironment(env, boundNames); + var bodyEnv = LocalEnvironment.forExpansion(env, boundNames); SyntaxWrap localWrap = bodyEnv.getWrap(); // Expand the bound-value expressions @@ -107,10 +107,7 @@ CompiledForm compile(Compiler comp, Environment env, SyntaxSexp stx) var bindingForms = (SyntaxSequence) stx.get(eval, 1); var binders = collectBinders(eval, bindingForms); - if (binders.length != 0) - { - env = LocalEnvironment.forBinders(env, binders); - } + env = LocalEnvironment.forCompilation(env, binders); final int numBindings = binders.length; CompiledForm [] valueForms = new CompiledForm [numBindings]; @@ -168,7 +165,7 @@ public Object doEval(Evaluator eval, Store store) Object[] boundValues = new Object[numBindings]; Arrays.fill(boundValues, UNDEF); - Store localStore = new LocalStore(store, boundValues); + Store localStore = LocalStore.forExecution(store, boundValues); for (int i = 0; i < numBindings; i++) { diff --git a/runtime/src/main/java/dev/ionfusion/fusion/LocalEnvironment.java b/runtime/src/main/java/dev/ionfusion/fusion/LocalEnvironment.java index b3f36a31..973ca570 100644 --- a/runtime/src/main/java/dev/ionfusion/fusion/LocalEnvironment.java +++ b/runtime/src/main/java/dev/ionfusion/fusion/LocalEnvironment.java @@ -130,61 +130,64 @@ Binding resolveMaybe(BaseSymbol name, /** Not null */ private final Environment myEnclosure; - private final Namespace myNamespace; private final LocalBinding[] myBindings; private final SyntaxWrap myWrap; - /** - * Expand-time environment construction. - */ - LocalEnvironment(Environment enclosure, SyntaxSymbol[] identifiers) + private LocalEnvironment(Environment enclosure, + LocalBinding[] bindings, + boolean makeWrap) { myEnclosure = enclosure; - myNamespace = enclosure.namespace(); + myBindings = bindings; + myWrap = makeWrap ? new LocalEnvironmentWrap() : null; + } + + /** + * Expand-time environment construction creates bindings for each identifier. + */ + static LocalEnvironment forExpansion(Environment enclosure, + SyntaxSymbol[] identifiers) + { // 2019-04 Stats from the standard library and test suite: // 85% of local envs have 1 entry, 12% have 2. // 99% have 3 or fewer entries and none have more than 5. int count = identifiers.length; - myBindings = new LocalBinding[count]; + var bindings = new LocalBinding[count]; for (int i = 0; i < count; i++) { SyntaxSymbol identifier = identifiers[i]; - myBindings[i] = new LocalBinding(identifier); + bindings[i] = new LocalBinding(identifier); } - myWrap = new LocalEnvironmentWrap(); + return new LocalEnvironment(enclosure, bindings, true); } /** - * Compile-time environment does not create bindings but uses them to - * determine access coordinates. + * Compile-time environment does not create bindings but uses them to determine + * variable addresses. + *
+ * Logic for empty layers must mirror {@link LocalStore#forExecution}. */ - private LocalEnvironment(Environment enclosure, LocalBinding[] bindings) + static Environment forCompilation(Environment enclosure, SyntaxSymbol[] binders) { - myEnclosure = enclosure; - myNamespace = enclosure.namespace(); - myBindings = bindings; - myWrap = null; - } + if (binders.length == 0) return enclosure; - static LocalEnvironment forBinders(Environment enclosure, SyntaxSymbol[] binders) - { LocalBinding[] bindings = new LocalBinding[binders.length]; for (int i = 0; i < binders.length; i++) { bindings[i] = (LocalBinding) binders[i].resolve(); } - return new LocalEnvironment(enclosure, bindings); + return new LocalEnvironment(enclosure, bindings, false); } @Override public Namespace namespace() { - return myNamespace; + return myEnclosure.namespace(); } public SyntaxWrap getWrap() diff --git a/runtime/src/main/java/dev/ionfusion/fusion/LocalStore.java b/runtime/src/main/java/dev/ionfusion/fusion/LocalStore.java index a4895808..85b58c8a 100644 --- a/runtime/src/main/java/dev/ionfusion/fusion/LocalStore.java +++ b/runtime/src/main/java/dev/ionfusion/fusion/LocalStore.java @@ -18,7 +18,7 @@ final class LocalStore * Caller can (and does) mutate the array elements after constructing the * store! */ - LocalStore(Store enclosure, Object[] values) + private LocalStore(Store enclosure, Object[] values) { assert values.length > 0; myEnclosure = enclosure; @@ -68,9 +68,15 @@ public void set(int rib, int address, Object value) //================================================================================== // Factories - public static Store forLocals(Store enclosure, Object[] locals) + + /** + * Logic for empty layers must mirror {@link LocalEnvironment#forCompilation}. + * + * @param values THE STORE ASSUMES OWNERSHIP OF THIS ARRAY. + */ + public static Store forExecution(Store enclosure, Object[] values) { - if (locals.length == 0) return enclosure; - return new LocalStore(enclosure, locals); + if (values.length == 0) return enclosure; + return new LocalStore(enclosure, values); } }