/// <summary> /// Emits code to prepare an evaluation stack for storing a reference into a variable. /// Supports operators chaining. Store is finished by calling <see cref="EmitAssign"/>. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeWriteRef(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; // Case 3: a_[x]_[x] never reached Debug.Assert(chain.IsArrayItem == false); // Case 4,5 never reached // 4: a[x]->... // 5: ...->a[]->... Debug.Assert(chain.IsMember == false); // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6, 7: ...->a[x]_[x]_ chain.Create(); chain.Begin(); // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that EmitAssign will finish the work (SetArrayItem or PhpArray.Add) } else { // 1, 2 Debug.Assert(this.isMemberOf == null); if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that further work will be done by EmitAssign (SetArrayItem or PhpArray.Add) } // 1: a_[x]_ // Do nothing now, let the work be done in EmitAssign() // Note further work will be done by EmitAssign (either SetItem or SetItemRef); } return PhpTypeCode.Unknown; }
/// <summary> /// Finishes the write operation starte by <see cref="Emit"/>. /// </summary> internal override PhpTypeCode EmitAssign(CodeGenerator/*!*/ codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; PhpTypeCode result; switch (access) { case AccessType.WriteAndReadRef: case AccessType.WriteAndReadUnknown: case AccessType.ReadAndWrite: case AccessType.ReadAndWriteAndReadRef: case AccessType.ReadAndWriteAndReadUnknown: case AccessType.Write: case AccessType.WriteRef: { bool reference = access == AccessType.WriteRef; // Note that some work was done in Emit() ! // In cases 3, 4, 5 EmitAssign is not called if (isMemberOf != null || (isMemberOf == null && (array is DirectStFldUse || array is IndirectStFldUse || array is ItemUse))) { // 2, 6, 7 chain.EmitSetArrayItem(indexTypeCode, index, reference); chain.End(); } else { // Note: The value which should be stored is already loaded on the evaluation stack. // Push the destination array and index and call the operator // 1: a_[x]_ Debug.Assert(array is SimpleVarUse); chain.IsArrayItem = true; chain.IsLastMember = true; indexTypeCode = codeGenerator.EmitArrayKey(chain, index); array.Emit(codeGenerator); chain.EmitSetItem(indexTypeCode, index, reference); // Store the changed variable into table of variables (do nothing in optimalized functions) ((SimpleVarUse)array).EmitLoadAddress_StoreBack(codeGenerator); } result = PhpTypeCode.Void; break; } case AccessType.None: // do nothing result = PhpTypeCode.Void; break; case AccessType.Read: // do nothing result = PhpTypeCode.Object; break; case AccessType.ReadRef: // Do nothing result = PhpTypeCode.PhpReference; break; default: Debug.Fail(); result = PhpTypeCode.Invalid; break; } return result; }
/// <summary> /// Emits code to prepare an evaluation stack for storing a value into a variable. /// Supports operators chaining. Store is finished by calling <see cref="EmitAssign"/>. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeWrite(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; if (chain.IsArrayItem) { // 3: a_[x]_[v] Debug.Assert(this.isMemberOf == null); return chain.EmitEnsureItem(array, index, true); } // 1, 2, 4, 5, 6, 7 if (chain.IsMember) { // 4, 5 if (this.isMemberOf != null) { // 5: ...->a[]->... // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; } else { // 4: a_[x]_->c->..., a[x]_[x]_->c->... chain.IsArrayItem = true; chain.IsLastMember = true; } PhpTypeCode result = chain.EmitEnsureItem(array, index, false); chain.IsArrayItem = false; return result; } // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6, 7: ...->a[x]_[x]_ chain.Create(); chain.Begin(); // Store isMemberOf for lazy emit chain.SetObjectForLazyEmit(this); chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that EmitAssign will finish the work (SetArrayItem or PhpArray.Add) return PhpTypeCode.Unknown; } // 1, 2 Debug.Assert(this.isMemberOf == null); if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; array.Emit(codeGenerator); indexTypeCode = codeGenerator.EmitArrayKey(chain, index); // Note that further work will be done by EmitAssign (SetArrayItem or PhpArray.Add) return PhpTypeCode.Unknown; } // 1: a_[x]_ // Do nothing now, let the work be done in EmitAssign() return PhpTypeCode.Unknown; }
/// <summary> /// Emit IL instructions that load the value of array index at the stack. /// </summary> internal PhpTypeCode EmitIndex(CodeGenerator/*!*/ codeGenerator) { return codeGenerator.EmitArrayKey(null, index); }