/// <summary> /// Emits code to load a reference to a variable onto an evaluation stack. Supports operators chaining. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeReadRef(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; LocalBuilder local = codeGenerator.IL.DeclareLocal(typeof(object)); // Case 3: a_[x]_[x] never reached Debug.Assert(chain.IsArrayItem == false, "ReadRef access shouldn't be set to array subchain nodes"); // Case 4,5 never reached // 4: a[x]->... // 5: ...->a[]->... Debug.Assert(chain.IsMember == false); // 1, 2, 6, 7 if (this.isMemberOf != null) { // last node of the field chain // // 6 , 7: ...->a[]_[]_ , ...->a_[]_ chain.Create(); chain.Begin(); if (this.isMemberOf is FunctionCall) { chain.LoadAddressOfFunctionReturnValue = true; } chain.SetObjectForLazyEmit(this); // let's emit the array subchain followed by the GetArrayItemRef: chain.IsArrayItem = true; chain.IsLastMember = false; chain.Lengthen(); // for own [] chain.EmitGetArrayItemRef(array, index); chain.IsArrayItem = false; chain.EndRef(); return(PhpTypeCode.PhpReference); } // 1, 2 if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse) { // we are at the beginning of the field chain // // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; chain.Lengthen(); chain.EmitGetArrayItemRef(array, index); chain.IsArrayItem = false; chain.EndRef(); return(PhpTypeCode.PhpReference); } // no chains // // 1: a_[x]_ return(chain.EmitGetItemRef((SimpleVarUse)array, index)); }
/// <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); }
internal override void EmitUnset(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; // Template: "unset(x[y])" Operators.UnsetItem(object obj,object index) // 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); chain.QuietRead = true; // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6 , 7: ...->a[]_[]_ , ...->a_[]_ chain.Create(); chain.Begin(); chain.Lengthen(); // for hop over -> isMemberOf.Emit(codeGenerator); chain.IsArrayItem = true; chain.IsLastMember = false; chain.EmitUnsetItem(array, index); chain.IsArrayItem = false; chain.End(); return; } // 1, 2 if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ chain.Create(); chain.Begin(); chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitUnsetItem(array, index); chain.IsArrayItem = false; chain.End(); return; } // 1: a_[x]_ chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitUnsetItem(array, index); chain.IsArrayItem = false; }
private PhpTypeCode EmitNodeWrite(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; if (chain.IsMember) { // 1,4,5,6,9 if (this.isMemberOf != null) { // 1: ...->a->... chain.Lengthen(); chain.EmitEnsureProperty(isMemberOf, this, false); return(PhpTypeCode.DObject); } if (chain.IsArrayItem) { // 4,6 if (chain.IsLastMember) { // 4: a[][] chain.EmitEnsureVariableIsArray(this); } else { // 6: $b->a[3] ChainBuilder.ObjectFieldLazyEmitInfo object_info = chain.GetObjectForLazyEmit(); // Lengthen for hop over -> chain.EmitEnsureProperty(object_info.ObjectForLazyEmit, this, true); chain.ReleaseObjectForLazyEmit(object_info); chain.IsArrayItem = true; chain.IsLastMember = false; } return(PhpTypeCode.PhpArray); } if (chain.Exists) { // 5: $a->b->c->... chain.EmitEnsureVariableIsObject(this); return(PhpTypeCode.DObject); } else { // 9: $a->b this.EmitLoadAddress(codeGenerator); return(PhpTypeCode.ObjectAddress); } } // 2,3,7,8 if (this.isMemberOf != null) { // 2: $b->a // 8: b[]->a chain.Create(); chain.Begin(); assignmentCallback = EmitWriteField(codeGenerator, false); // Note: more work is done in EmitAssign // some data are preloaded but nothing that can be consumed is loaded on stack: return(PhpTypeCode.Unknown); } // 3,7 if (codeGenerator.ChainBuilder.IsArrayItem) { // 3: a[3] EmitLoadAddress(codeGenerator); return(PhpTypeCode.ObjectAddress); } else { // 7: $a EmitStorePrepare(codeGenerator); return(PhpTypeCode.Unknown); } }
private PhpTypeCode EmitNodeWrite(IndirectVarUse node, CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; if (chain.IsMember) { // 1,4,5,6,9 if (node.IsMemberOf != null) { // 1: ...->$a->... chain.Lengthen(); chain.EmitEnsureProperty(node.IsMemberOf, node, false); return(PhpTypeCode.DObject); } if (chain.IsArrayItem) { // 4,6 if (chain.IsLastMember) { // 4: ${"a"}[][] chain.EmitEnsureVariableIsArray(node); return(PhpTypeCode.PhpArray); } else { // 6: $b->${"a"}[3] ChainBuilder.ObjectFieldLazyEmitInfo object_info = chain.GetObjectForLazyEmit(); // Lengthen for hop over -> chain.EmitEnsureProperty(object_info.ObjectForLazyEmit, node, true); chain.ReleaseObjectForLazyEmit(object_info); chain.IsArrayItem = true; chain.IsLastMember = false; return(PhpTypeCode.PhpArray); } } if (chain.Exists) { // 5: $$a->b->c->... chain.EmitEnsureVariableIsObject(node); return(PhpTypeCode.DObject); } else { // 9: $$a->b this.EmitLoadAddress(node, codeGenerator); return(PhpTypeCode.ObjectAddress); } } // 2,3,7,8 if (node.IsMemberOf != null) { // 2: $b->a // 8: b[]->a chain.Create(); chain.Begin(); assignmentCallback = EmitWriteField(node, codeGenerator, false); // Note: more work is done in EmitAssign return(PhpTypeCode.Unknown); } // 3,7 if (codeGenerator.ChainBuilder.IsArrayItem) { // 3: ${"a"}[3] this.EmitLoadAddress(node, codeGenerator); return(PhpTypeCode.ObjectAddress); } // 7: $a //codeGenerator.EmitVariableStorePrepare(this); this.EmitStorePrepare(node, codeGenerator); return(PhpTypeCode.Unknown); }
/// <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> /// Emits code to load <see cref="PhpRuntimeChain"/> onto an evaluation stack. Supports operators chaining. /// </summary> /// <param name="codeGenerator"></param> private PhpTypeCode EmitNodeReadUnknown(CodeGenerator codeGenerator) { ChainBuilder chain = codeGenerator.ChainBuilder; PhpTypeCode result = PhpTypeCode.PhpRuntimeChain; if (chain.IsArrayItem) { // 3: a_[x]_[x] chain.Lengthen(); // for [] chain.EmitRTChainAddItem(this); return(result); } // 1,2,4,5,6,7 if (chain.IsMember) { // 4, 5 if (this.isMemberOf != null) { // 5: ...->a[]->... // Lengthen chain for isMemberOf chain.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); chain.EmitCreateRTChain(); } // Lengthen chain for own [] chain.Lengthen(); chain.IsArrayItem = true; chain.IsLastMember = false; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; return(result); } // 4: a[x]->... // Lengthen chain for itself chain.Lengthen(); // for own [] chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; return(result); } // 1, 2, 6, 7 if (this.isMemberOf != null) { // 6 , 7: ...->a[]_[]_ , ...->a_[]_ bool quiet_read = chain.QuietRead; chain.Create(); chain.Begin(); chain.QuietRead = quiet_read; chain.Lengthen(); // for hop over -> PhpTypeCode res = isMemberOf.Emit(codeGenerator); if (res != PhpTypeCode.PhpRuntimeChain) { codeGenerator.EmitBoxing(res); chain.EmitCreateRTChain(); } chain.IsArrayItem = true; chain.IsLastMember = false; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; chain.End(); return(result); } // 1, 2 if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse /* ??? */) { // 2: a[]_[]_ bool quiet_read = chain.QuietRead; chain.Create(); chain.Begin(); chain.QuietRead = quiet_read; chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; chain.End(); return(result); } // 1: a_[x]_ chain.IsArrayItem = true; chain.IsLastMember = true; chain.EmitRTChainAddItem(this); chain.IsArrayItem = false; return(result); }
/// <summary> /// Emits code to load variable onto the evaluation stack. Supports operators chaining. /// </summary> /// <param name="codeGenerator">A geenrator.</param> /// <param name="itemGetterKind">Whether to load for "get", "isset", or "empty".</param> private PhpTypeCode EmitNodeRead(CodeGenerator /*!*/ codeGenerator, Operators.GetItemKinds itemGetterKind) { ChainBuilder chain = codeGenerator.ChainBuilder; PhpTypeCode result; if (chain.IsArrayItem) { // we are in the array subchain // // 3: a_[x]_[x] chain.Lengthen(); // for [] result = chain.EmitGetItem(array, index, itemGetterKind); return(result); } // 1,2,4,5,6,7 if (chain.IsMember) { // we are in the field chain // // 4, 5 if (this.isMemberOf != null) { // we are in the middle of the field chain // // 5: ...->a[]->... // Lengthen chain for isMemberOf chain.Lengthen(); // for hop over -> isMemberOf.Emit(codeGenerator); // Lengthen chain for own [] chain.Lengthen(); chain.IsArrayItem = true; chain.IsLastMember = false; result = chain.EmitGetItem(array, index, itemGetterKind); chain.IsArrayItem = false; return(result); } else { // we are at the beginning of the field chain // // 4: a[x]->... // Lengthen chain for itself chain.Lengthen(); // for own [] chain.IsArrayItem = true; chain.IsLastMember = true; result = chain.EmitGetItem(array, index, itemGetterKind); chain.IsArrayItem = false; return(result); } } // 1, 2, 6, 7 if (this.isMemberOf != null) { // last node of the field chain // // 6 , 7: ...->a[]_[]_ , ...->a_[]_ bool quiet_read = chain.QuietRead; chain.Create(); chain.Begin(); chain.QuietRead = quiet_read; chain.Lengthen(); // for hop over -> isMemberOf.Emit(codeGenerator); // let's emit the array subchain followed by the GetItem: chain.IsArrayItem = true; chain.IsLastMember = false; result = chain.EmitGetItem(array, index, itemGetterKind); chain.IsArrayItem = false; chain.End(); return(result); } // 1, 2 if (array is ItemUse || array is DirectStFldUse || array is IndirectStFldUse) { // we are at the beginning of the field chain // // 2: a[]_[]_ bool quiet_read = chain.QuietRead; chain.Create(); chain.Begin(); chain.QuietRead = quiet_read; chain.IsArrayItem = true; chain.IsLastMember = true; result = chain.EmitGetItem(array, index, itemGetterKind); chain.IsArrayItem = false; chain.End(); return(result); } // no chains // // 1: a_[x]_ chain.IsArrayItem = true; chain.IsLastMember = true; result = chain.EmitGetItem(array, index, itemGetterKind); chain.IsArrayItem = false; return(result); }