diff --git a/packages/@glimmer/opcode-compiler/lib/opcode-builder.ts b/packages/@glimmer/opcode-compiler/lib/opcode-builder.ts index b7bd0924b5..ed325612a4 100644 --- a/packages/@glimmer/opcode-compiler/lib/opcode-builder.ts +++ b/packages/@glimmer/opcode-compiler/lib/opcode-builder.ts @@ -882,8 +882,13 @@ export abstract class OpcodeBuilder { } } + pushBlockScope(): void { + this.push(Op.PushBlockScope); + } + pushYieldableBlock(block: Option): void { this.pushSymbolTable(block && block.symbolTable); + this.pushBlockScope(); this.pushBlock(block); } diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts index ef99143a8b..80971fab5d 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts @@ -4,6 +4,7 @@ import { Tag, TagWrapper, VersionedPathReference, Reference } from "@glimmer/ref import { Arguments } from '../../vm/arguments'; import { ComponentState } from './component'; import { ComponentManager } from '../../internal-interfaces'; +import { Scope } from '../../environment'; import { BlockSymbolTable } from "@glimmer/interfaces"; import { ICompilableTemplate } from "@glimmer/opcode-compiler"; @@ -17,6 +18,8 @@ export const CheckReference: Checker = export const CheckArguments = CheckInstanceof(Arguments); +export const CheckScope = CheckInstanceof(Scope); + export const CheckComponentManager: Checker = CheckInterface({ getCapabilities: CheckFunction }); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts index 33ff7ec9fe..d9ff74a117 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts @@ -20,7 +20,7 @@ import { PublicComponentSpec } from '../../component/interfaces'; import { normalizeStringValue } from '../../dom/normalize'; -import { DynamicScope, ScopeBlock, ScopeSlot } from '../../environment'; +import { DynamicScope, ScopeBlock, ScopeSlot, Scope } from '../../environment'; import { APPEND_OPCODES, UpdatingOpcode } from '../../opcodes'; import { UNDEFINED_REFERENCE } from '../../references'; import { UpdatingVM, VM } from '../../vm'; @@ -33,7 +33,7 @@ import { check, expectStackChange, CheckInstanceof, CheckFunction, CheckInterfac import { Op, Register } from '@glimmer/vm'; import { TemplateMeta } from "@glimmer/wire-format"; import { ATTRS_BLOCK } from '@glimmer/opcode-compiler'; -import { CheckReference, CheckArguments, CheckPathReference, CheckComponentState, CheckCompilableBlock } from './-debug-strip'; +import { CheckReference, CheckArguments, CheckPathReference, CheckComponentState, CheckScope, CheckCompilableBlock } from './-debug-strip'; const ARGS = new Arguments(); @@ -441,9 +441,10 @@ APPEND_OPCODES.add(Op.InvokeComponentLayout, vm => { let bindBlock = (name: string) => { let symbol = symbols.indexOf(name); let handle = check(stack.pop(), CheckOr(CheckOption(CheckHandle), CheckOption(CheckCompilableBlock))); + let blockScope = check(stack.pop(), CheckOption(CheckScope)) as Option; // FIXME(mmun): shouldn't need to cast this let table = check(stack.pop(), CheckOption(CheckBlockSymbolTable)); - let block: Option = table ? [handle!, table] : null; + let block: Option = table ? [handle!, blockScope!, table] : null; if (symbol !== -1) { scope.bindBlock(symbol + 1, block); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts index 8996198267..f5d2704205 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts @@ -1,7 +1,7 @@ import { Opaque, Option } from '@glimmer/interfaces'; import { VersionedPathReference } from '@glimmer/reference'; import { Op } from '@glimmer/vm'; -import { ScopeBlock } from '../../environment'; +import { Scope, ScopeBlock } from '../../environment'; import { APPEND_OPCODES } from '../../opcodes'; import { FALSE_REFERENCE, TRUE_REFERENCE } from '../../references'; import { PublicVM } from '../../vm'; @@ -9,7 +9,7 @@ import { ConcatReference } from '../expressions/concat'; import { assert } from "@glimmer/util"; import { check, expectStackChange, CheckFunction, CheckOption, CheckHandle, CheckBlockSymbolTable, CheckOr } from '@glimmer/debug'; import { stackAssert } from './assert'; -import { CheckArguments, CheckPathReference, CheckCompilableBlock } from './-debug-strip'; +import { CheckArguments, CheckPathReference, CheckCompilableBlock, CheckScope } from './-debug-strip'; export type FunctionExpression = (vm: PublicVM) => VersionedPathReference; @@ -36,9 +36,10 @@ APPEND_OPCODES.add(Op.SetVariable, (vm, { op1: symbol }) => { APPEND_OPCODES.add(Op.SetBlock, (vm, { op1: symbol }) => { let handle = check(vm.stack.pop(), CheckOr(CheckOption(CheckHandle), CheckCompilableBlock)); + let scope = check(vm.stack.pop(), CheckScope) as Option; // FIXME(mmun): shouldn't need to cast this let table = check(vm.stack.pop(), CheckOption(CheckBlockSymbolTable)); - let block: Option = table ? [handle!, table] : null; + let block: Option = table ? [handle!, scope!, table] : null; vm.scope().bindBlock(symbol, block); }); @@ -70,14 +71,16 @@ APPEND_OPCODES.add(Op.GetBlock, (vm, { op1: _block }) => { let block = vm.scope().getBlock(_block); if (block) { + stack.push(block[2]); stack.push(block[1]); stack.push(block[0]); } else { stack.push(null); stack.push(null); + stack.push(null); } - expectStackChange(vm.stack, 2, 'GetBlock'); + expectStackChange(vm.stack, 3, 'GetBlock'); }); APPEND_OPCODES.add(Op.HasBlock, (vm, { op1: _block }) => { @@ -86,7 +89,9 @@ APPEND_OPCODES.add(Op.HasBlock, (vm, { op1: _block }) => { }); APPEND_OPCODES.add(Op.HasBlockParams, (vm) => { + // FIXME(mmun): should only need to push the symbol table check(vm.stack.pop(), CheckOption(CheckOr(CheckHandle, CheckCompilableBlock))); + check(vm.stack.pop(), CheckOption(CheckScope)); let table = check(vm.stack.pop(), CheckOption(CheckBlockSymbolTable)); assert(table === null || (table && typeof table === 'object' && Array.isArray(table.parameters)), stackAssert('Option', table)); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts index ef38318b66..4a53e84e37 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts @@ -12,12 +12,13 @@ import { initializeGuid, assert } from '@glimmer/util'; import { expectStackChange, CheckNumber, check, CheckInstanceof, CheckOption, CheckBlockSymbolTable, CheckHandle, CheckPrimitive } from '@glimmer/debug'; import { stackAssert } from './assert'; import { APPEND_OPCODES, UpdatingOpcode } from '../../opcodes'; +import { Scope } from '../../environment'; import { PrimitiveReference } from '../../references'; import { CompilableTemplate } from '../../syntax/interfaces'; import { VM, UpdatingVM } from '../../vm'; import { Arguments } from '../../vm/arguments'; import { LazyConstants, PrimitiveType } from "@glimmer/program"; -import { CheckReference } from './-debug-strip'; +import { CheckReference, CheckScope } from './-debug-strip'; APPEND_OPCODES.add(Op.ChildScope, vm => vm.pushChildScope()); @@ -108,6 +109,11 @@ APPEND_OPCODES.add(Op.PushSymbolTable, (vm, { op1: _table }) => { stack.push(vm.constants.getSymbolTable(_table)); }); +APPEND_OPCODES.add(Op.PushBlockScope, (vm) => { + let stack = vm.stack; + stack.push(vm.scope()); +}); + APPEND_OPCODES.add(Op.CompileBlock, vm => { let stack = vm.stack; let block = stack.pop | 0>(); @@ -128,6 +134,7 @@ APPEND_OPCODES.add(Op.InvokeYield, vm => { let { stack } = vm; let handle = check(stack.pop(), CheckOption(CheckHandle)); + let scope = check(stack.pop(), CheckOption(CheckScope)) as Option; // FIXME(mmun): shouldn't need to cast this let table = check(stack.pop(), CheckOption(CheckBlockSymbolTable)); assert(table === null || (table && typeof table === 'object' && Array.isArray(table.parameters)), stackAssert('Option', table)); @@ -139,25 +146,30 @@ APPEND_OPCODES.add(Op.InvokeYield, vm => { // To balance the pop{Frame,Scope} vm.pushFrame(); - vm.pushCallerScope(); - + vm.pushScope(scope!); // Could be null but it doesnt matter as it is immediatelly popped. return; } - let locals = table.parameters; - let localsCount = locals.length; + let invokingScope = scope!; - vm.pushCallerScope(localsCount > 0); + // If necessary, create a child scope + { + let locals = table.parameters; + let localsCount = locals.length; - let scope = vm.scope(); + if (localsCount > 0) { + invokingScope = invokingScope.child(); - for (let i=0; i, BlockSymbolTable]; +export type ScopeBlock = [VMHandle | ICompilableTemplate, Scope, BlockSymbolTable]; export type ScopeSlot = Option> | Option; export interface DynamicScope { diff --git a/packages/@glimmer/runtime/lib/vm/append.ts b/packages/@glimmer/runtime/lib/vm/append.ts index 2a45c6ec81..dfa81d1fc7 100644 --- a/packages/@glimmer/runtime/lib/vm/append.ts +++ b/packages/@glimmer/runtime/lib/vm/append.ts @@ -372,11 +372,6 @@ export default class VM implements PublicVM { this.scopeStack.push(this.scope().child()); } - pushCallerScope(childScope = false) { - let callerScope = expect(this.scope().getCallerScope(), 'pushCallerScope is called when a caller scope is present'); - this.scopeStack.push(childScope ? callerScope.child() : callerScope); - } - pushDynamicScope(): DynamicScope { let child = this.dynamicScope().child(); this.dynamicScopeStack.push(child); @@ -390,6 +385,10 @@ export default class VM implements PublicVM { return scope; } + pushScope(scope: Scope) { + this.scopeStack.push(scope); + } + popScope() { this.scopeStack.pop(); } diff --git a/packages/@glimmer/vm/lib/-debug-strip.ts b/packages/@glimmer/vm/lib/-debug-strip.ts index 987bbc7f4f..3a46e597ee 100644 --- a/packages/@glimmer/vm/lib/-debug-strip.ts +++ b/packages/@glimmer/vm/lib/-debug-strip.ts @@ -220,6 +220,11 @@ OPCODE_METADATA(Op.PushSymbolTable, { stackChange: 1 }); +OPCODE_METADATA(Op.PushBlockScope, { + name: 'PushBlockScope', + stackChange: 1 +}); + OPCODE_METADATA(Op.CompileBlock, { name: 'CompileBlock' }); @@ -237,7 +242,8 @@ OPCODE_METADATA(Op.InvokeStatic, { OPCODE_METADATA(Op.InvokeYield, clearsArgs({ name: 'InvokeYield', - argsPosition: 2 + argsPosition: 3, + netPops: 1 })); OPCODE_METADATA(Op.Jump, { @@ -584,7 +590,7 @@ OPCODE_METADATA(Op.SetBlock, { name: 'SetBlock', ops: [ScopeSymbol('symbol')], operands: 1, - stackChange: -2 + stackChange: -3 }); OPCODE_METADATA(Op.GetVariable, { @@ -604,7 +610,7 @@ OPCODE_METADATA(Op.GetBlock, { name: 'GetBlock', ops: [ScopeBlock('block')], operands: 1, - stackChange: 2 + stackChange: 3 }); OPCODE_METADATA(Op.HasBlock, { @@ -617,7 +623,7 @@ OPCODE_METADATA(Op.HasBlock, { OPCODE_METADATA(Op.HasBlockParams, { name: 'HasBlockParams', ops: [ScopeBlock('block')], - stackChange: -1 + stackChange: -2 }); OPCODE_METADATA(Op.Concat, { diff --git a/packages/@glimmer/vm/lib/opcodes.ts b/packages/@glimmer/vm/lib/opcodes.ts index c0c96d73ab..7ebce250fc 100644 --- a/packages/@glimmer/vm/lib/opcodes.ts +++ b/packages/@glimmer/vm/lib/opcodes.ts @@ -432,6 +432,16 @@ export const enum Op { */ CompileBlock, + /** + * Operation: Push a scope onto the stack. + * Format: + * (PushBlockScope #Scope) + * Operand Stack: + * ..., → + * ..., Scope + */ + PushBlockScope, + /** * Operation: Push a symbol table onto the stack. * Format: