|
|
|
@ -42,24 +42,27 @@ trait AssignOpTrait |
|
|
|
$tmp = $this->genTmpVarName(); |
|
|
|
$tmp = $this->genTmpVarName(); |
|
|
|
$this->addLocalVar($tmp, Type::VAR); |
|
|
|
$this->addLocalVar($tmp, Type::VAR); |
|
|
|
|
|
|
|
|
|
|
|
// 仅当目标是 php::Array 时使用 item/newItem: |
|
|
|
// item(dim, true) updates an existing reference's value, while offsetSet() |
|
|
|
// - item(dim, true) 直接返回元素 zval 地址,赋值时能穿透 IS_REFERENCE 写回, |
|
|
|
// replaces the array bucket and breaks the reference. Keep offsetSet() for |
|
|
|
// 修复 $arr = [&$x] / $arr[] = &$x 这类数组元素引用的写回问题; |
|
|
|
// ArrayAccess objects; dynamically typed/reference containers need a |
|
|
|
// - 对于 ArrayAccess 对象(如 ArrayObject)或类型未知(VAR)的变量,item 不存在或语义不符, |
|
|
|
// runtime array check because either representation is possible. |
|
|
|
// 必须继续使用 offsetSet(对象数组元素的引用写回由对象自身保证,编译器不负责)。 |
|
|
|
$arrayType = $this->getVarType($array); |
|
|
|
$isPhpArray = $this->getVarType($array) === Type::ARRAY; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($left->dim === null) { |
|
|
|
if ($left->dim === null) { |
|
|
|
if ($isPhpArray) { |
|
|
|
|
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.newItem() = {$tmp}" . '), ' . $tmp . ')'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')'; |
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet(" . self::VALUE_NULL . ", {$tmp})" . '), ' . $tmp . ')'; |
|
|
|
} |
|
|
|
} |
|
|
|
$dim = $this->parseIdentifier($left->dim); |
|
|
|
$dim = $this->parseIdentifier($left->dim); |
|
|
|
|
|
|
|
|
|
|
|
if ($isPhpArray) { |
|
|
|
if ($arrayType === Type::ARRAY) { |
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.item({$dim}, true) = {$tmp}" . '), ' . $tmp . ')'; |
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.item({$dim}, true) = {$tmp}" . '), ' . $tmp . ')'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ($arrayType === Type::VAR || $arrayType === Type::REF) { |
|
|
|
|
|
|
|
$writeArray = "static_cast<void>({$array}.item({$dim}, true) = {$tmp})"; |
|
|
|
|
|
|
|
$writeOther = "{$array}.offsetSet({$dim}, {$tmp})"; |
|
|
|
|
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' |
|
|
|
|
|
|
|
. "({$array}.isArray() ? {$writeArray} : {$writeOther})" |
|
|
|
|
|
|
|
. '), ' . $tmp . ')'; |
|
|
|
|
|
|
|
} |
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet({$dim}, {$tmp})" . '), ' . $tmp . ')'; |
|
|
|
return $code . '((' . $tmp . ' = ' . $value . ', ' . "{$array}.offsetSet({$dim}, {$tmp})" . '), ' . $tmp . ')'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -759,6 +762,9 @@ trait AssignOpTrait |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$left = $this->parseWritableIdentifier($expr->var); |
|
|
|
$left = $this->parseWritableIdentifier($expr->var); |
|
|
|
|
|
|
|
// Keep this write-context form for every RHS kind. Re-parsing it as a |
|
|
|
|
|
|
|
// read later breaks append and missing-key targets such as |
|
|
|
|
|
|
|
// `$array[] =& $source`. |
|
|
|
|
|
|
|
|
|
|
|
if ($this->isVarExpr($expr->var)) { |
|
|
|
if ($this->isVarExpr($expr->var)) { |
|
|
|
if (!$this->hasVar($left)) { |
|
|
|
if (!$this->hasVar($left)) { |
|
|
|
@ -835,15 +841,10 @@ trait AssignOpTrait |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} elseif ($this->isPropertyFetch($expr->expr)) { |
|
|
|
} elseif ($this->isPropertyFetch($expr->expr)) { |
|
|
|
$left = $this->parseIdentifier($expr->var); |
|
|
|
|
|
|
|
$rightExpr = $tmpVar . ' = ' . $this->emitDynamicPropertyFetchRef($expr->expr, $expr); |
|
|
|
$rightExpr = $tmpVar . ' = ' . $this->emitDynamicPropertyFetchRef($expr->expr, $expr); |
|
|
|
} elseif ($this->isStaticPropertyFetch($expr->expr)) { |
|
|
|
} elseif ($this->isStaticPropertyFetch($expr->expr)) { |
|
|
|
$left = $this->parseIdentifier($expr->var); |
|
|
|
|
|
|
|
$rightExpr = $tmpVar . ' = ' . $this->emitStaticPropertyFetchRef($expr->expr, $expr); |
|
|
|
$rightExpr = $tmpVar . ' = ' . $this->emitStaticPropertyFetchRef($expr->expr, $expr); |
|
|
|
} elseif ($this->isArrayDimFetch($expr->expr)) { |
|
|
|
} elseif ($this->isArrayDimFetch($expr->expr)) { |
|
|
|
// $left 已在函数开头通过 parseWritableIdentifier($expr->var) 正确计算, |
|
|
|
|
|
|
|
// 这里不可再用 parseIdentifier() 覆盖,否则当左值是数组追加($arr[] = &$x) |
|
|
|
|
|
|
|
// 或数组元素($arr[$k] = &$x)时会被当作读取而报错 "Cannot use [] for reading"。 |
|
|
|
|
|
|
|
$array = $this->parseWritableIdentifier($expr->expr->var); |
|
|
|
$array = $this->parseWritableIdentifier($expr->expr->var); |
|
|
|
if ($expr->expr->dim == null) { |
|
|
|
if ($expr->expr->dim == null) { |
|
|
|
$this->fatalError($expr, 'Cannot assign reference to array dim fetch without dim'); |
|
|
|
$this->fatalError($expr, 'Cannot assign reference to array dim fetch without dim'); |
|
|
|
|