PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/src/com/didactilab/gwt/phprpc/phpderpc/phprpc/derpc/WebModePayloadSink.php

http://gwtphp-derpc.googlecode.com/
PHP | 791 lines | 632 code | 124 blank | 35 comment | 55 complexity | fa74ca23f4abbda9ea858c2d0c2c1ed4 MD5 | raw file
  1. <?php
  2. /*
  3. * Copyright 2011 DidactiLab SAS
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  6. * use this file except in compliance with the License. You may obtain a copy of
  7. * the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. * License for the specific language governing permissions and limitations under
  15. * the License.
  16. *
  17. * Date: 30 avr. 2011
  18. * Author: Mathieu LIGOCKI
  19. */
  20. require_once PHPRPC_ROOT . 'derpc/ast.php';
  21. require_once PHPRPC_ROOT . 'classes.php';
  22. require_once PHPRPC_ROOT . 'stream.php';
  23. require_once PHPRPC_ROOT . 'exception.php';
  24. require_once PHPRPC_ROOT . 'derpc/EscapeUtil.php';
  25. class BackRefAssigner extends RpcCommandVisitor {
  26. private $seenOnce;
  27. private $parent;
  28. public function __construct(WebModePayloadSink $parent) {
  29. $this->seenOne = new HashSet();
  30. $this->parent = $parent;
  31. }
  32. public function endVisitInvoke(InvokeCustomFieldSerializerCommand $x, Context $ctx) {
  33. $this->parent->makeBackRef($x);
  34. }
  35. public function endVisitLong(LongValueCommand $x, Context $ctx) {
  36. $this->process($x);
  37. }
  38. public function endVisitString(StringValueCommand $x, Context $ctx) {
  39. $this->process($x);
  40. }
  41. public function visitArray(ArrayValueCommand $x, Context $ctx) {
  42. return $this->process($x);
  43. }
  44. public function visitInstantiate(InstantiateCommand $x, Context $ctx) {
  45. return $this->process($x);
  46. }
  47. private function process(ValueCommand $x) {
  48. if (!$this->seenOne->add($x)) {
  49. $this->parent->makeBackRef($x);
  50. return false;
  51. }
  52. return true;
  53. }
  54. }
  55. class WebModePayloadVisitor extends RpcCommandVisitor {
  56. private $parent;
  57. private $constructorFunctions;
  58. private $commandBuffers;
  59. private $currentBuffer = null;
  60. private $stack = array();
  61. private $started;
  62. private $clientOracle;
  63. public function __construct(WebModePayloadSink $parent) {
  64. $this->parent = $parent;
  65. $this->constructorFunctions = new ObjectMap();
  66. $this->commandBuffers = new ObjectMap();
  67. $this->started = new HashSet();
  68. $this->clientOracle = $parent->getClientOracle();
  69. }
  70. public function endVisitBoolean(BooleanValueCommand $x, Context $ctx) {
  71. if ($x->getValue()) {
  72. $this->one();
  73. }
  74. else {
  75. $this->zero();
  76. }
  77. }
  78. public function endVisitByte(ByteValueCommand $x, Context $ctx) {
  79. $this->push(String::valueOf($x->getValue()));
  80. }
  81. public function endVisitChar(CharValueCommand $x, Context $ctx) {
  82. $this->push(String::valueOf(Character::ord($x->getValue())));
  83. }
  84. public function endVisitDouble(DoubleValueCommand $x, Context $ctx) {
  85. $this->push(String::valueOf($x->getValue()));
  86. }
  87. public function endVisitEnum(EnumValueCommand $x, Context $ctx) {
  88. $constantName = $x->getClass()->getConstantNameByValue($x->getValue());
  89. $fieldName = $this->clientOracle->getFieldId($x->getClass(), $constantName);
  90. if ($fieldName == null) {
  91. throw new IncompatibleRemoteServiceException('The client cannot accept ' . $constantName);
  92. }
  93. $clinitName = $this->clientOracle->getMethodId($x->getClass(), '$clinit', array());
  94. assert(!is_null($clinitName));
  95. // (clinit(), A)
  96. $this->lparen();
  97. $this->push($clinitName);
  98. $this->lparen();
  99. $this->rparen();
  100. $this->comma();
  101. $this->push($fieldName);
  102. $this->rparen();
  103. }
  104. public function endVisitFloat(FloatValueCommand $x, Context $ctx) {
  105. $this->push(String::valueOf($x->getValue()));
  106. }
  107. public function endVisitInt(IntValueCommand $x, Context $ctx) {
  108. $this->push(String::valueOf($x->getValue()));
  109. }
  110. public function endVisitLong(LongValueCommand $x, Context $ctx) {
  111. $fieldValue = $x->getValue();
  112. $l = $fieldValue & 0x3FFFFF;
  113. $m = ($fieldValue / 4194304) & 0x3FFFFF;
  114. $h = ($fieldValue / 17592186044416) & 0xFFFFF;
  115. $this->push('{l:' . $l . ',m:' . $m . ',h:' . $h . '}');
  116. }
  117. public function endVisitNull(NullValueCommand $x, Context $ctx) {
  118. $this->_null();
  119. }
  120. public function endVisitShort(ShortValueCommand $x, Context $ctx) {
  121. $this->push(String::valueOf($x->getValue()));
  122. }
  123. public function endVisitString(StringValueCommand $x, Context $ctx) {
  124. if ($this->parent->hasBackRef($x)) {
  125. if (!$this->isStarted($x)) {
  126. $escaped = EscapeUtil::escape($x->getValue());
  127. $this->push($this->beginValue($x));
  128. $this->eq();
  129. $this->quote();
  130. $this->push($escaped);
  131. $this->quote();
  132. $this->commit($x, false);
  133. }
  134. else {
  135. $this->push($this->parent->makeBackRef($x));
  136. }
  137. }
  138. else {
  139. $escaped = EscapeUtil::escape($x->getValue());
  140. $this->quote();
  141. $this->push($escaped);
  142. $this->quote();
  143. }
  144. }
  145. public function visitArray(ArrayValueCommand $x, Context $ctx) {
  146. $hasBackRef = $this->parent->hasBackRef($x);
  147. if ($hasBackRef && $this->isStarted($x)) {
  148. $this->push($this->parent->makeBackRef($x));
  149. return false;
  150. }
  151. // constructorFunction(x = [value,value,value])
  152. $currentBackRef = $this->beginValue($x);
  153. $this->push($this->constructorFunctionArray($x));
  154. $this->lparen();
  155. if ($hasBackRef) {
  156. $this->push($currentBackRef);
  157. $this->eq();
  158. }
  159. $this->lbracket();
  160. $values = $x->getComponentValues();
  161. for ($i=0; $i<count($values); $i++) {
  162. $this->accept($values[$i]);
  163. if ($i < count($values) - 1) {
  164. $this->comma();
  165. }
  166. }
  167. $this->rbracket();
  168. $this->rparen();
  169. $this->commit($x, false);
  170. if (!$hasBackRef) {
  171. $this->parent->forget($x);
  172. }
  173. return false;
  174. }
  175. public function visitInstantiate(InstantiateCommand $x, Context $ctx) {
  176. $hasBackRef = $this->parent->hasBackRef($x);
  177. if ($hasBackRef && $this->isStarted($x)) {
  178. $this->push($this->parent->makeBackRef($x));
  179. return false;
  180. }
  181. $currentBackRef = $this->beginValue($x);
  182. $constructorFunction = $this->constructorFunctionInstantiate($x);
  183. $seedName = $this->clientOracle->getSeedName($x->getTargetClass());
  184. if (is_null($seedName)) {
  185. throw new IncompatibleRemoteServiceException('The client cannot create type ' . $x->getTargetClass()->getName());
  186. }
  187. // constructorFunctionFoo(x = new Foo, field1, field2)
  188. $this->push($constructorFunction);
  189. $this->lparen();
  190. if ($hasBackRef) {
  191. $this->push($currentBackRef);
  192. $this->eq();
  193. }
  194. $this->_new();
  195. $this->push($seedName);
  196. foreach ($x->getSetters() as $setter) {
  197. $this->comma();
  198. $this->accept($setter->getValue());
  199. }
  200. $this->rparen();
  201. $this->commit($x, false);
  202. if (!$hasBackRef) {
  203. $this->parent->forget($x);
  204. }
  205. return false;
  206. }
  207. public function visitInvoke(InvokeCustomFieldSerializerCommand $x, Context $ctx) {
  208. if ($this->isStarted($x)) {
  209. $this->push($this->parent->makeBackRef($x));
  210. return false;
  211. }
  212. $currentBackRef = $this->beginValue($x);
  213. $this->lparen();
  214. $makeReader = new InstantiateCommand(Classes::classOf('CommandClientSerializationStreamReader'));
  215. $this->parent->makeBackRef($makeReader);
  216. $payload = new ArrayValueCommand(Classes::classOf('Object'));
  217. foreach ($x->getValues() as $value) {
  218. $payload->add($value);
  219. }
  220. $makeReader->set(Classes::classOf('CommandClientSerializationStreamReader'), 'payload', $payload);
  221. $instantiateIdent = $this->clientOracle->getMethodId($x->getSerializerClass(),
  222. 'instantiate',
  223. array(Classes::classOf('SerializationStreamReader')));
  224. // x = new Foo,
  225. // x = instantiate(reader),
  226. $this->push($currentBackRef);
  227. $this->eq();
  228. if (is_null($instantiateIdent)) {
  229. // No instantiate method, we'll have to invoke the constructor
  230. // new Foo()
  231. if (is_null($x->getTargetClass()->getEnclosingClass())) {
  232. $constructorMethodName = $x->getTargetClass()->getName();
  233. }
  234. else {
  235. $name = $x->getTargetClass()->getFullName();
  236. $constructorMethodName = mb_substr($name, mb_strrpos($name, '.') + 1);
  237. }
  238. $constructorIdent = $this->clientOracle->getMethodId($x->getTargetClass(),
  239. $constructorMethodName, array());
  240. assert(!is_null($constructorIdent));
  241. // new contructor,
  242. $this->_new();
  243. $this->push($constructorIdent);
  244. $this->comma();
  245. }
  246. else {
  247. // instantiate(reader)
  248. $this->push($instantiateIdent);
  249. $this->lparen();
  250. $this->accept($makeReader);
  251. $this->rparen();
  252. $this->comma();
  253. }
  254. $deserializeIdent = $this->clientOracle->getMethodId(
  255. $x->getSerializerClass(), 'deserialize',
  256. array(Classes::classOf('SerializationStreamReader'), $x->getManuallySerializedType()));
  257. if ($deserializeIdent != null) {
  258. // deserializer(reader, obj),
  259. $this->push($deserializeIdent);
  260. $this->lparen();
  261. $this->accept($makeReader);
  262. $this->comma();
  263. $this->push($currentBackRef);
  264. $this->rparen();
  265. $this->comma();
  266. }
  267. foreach ($x->getSetters() as $setter) {
  268. $this->accept($setter);
  269. $this->comma();
  270. }
  271. $this->push($currentBackRef);
  272. $this->rparen();
  273. $this->commit($x, false);
  274. $this->parent->forget($makeReader);
  275. return false;
  276. }
  277. public function visitReturn(ReturnCommand $x, Context $ctx) {
  278. $values = $x->getValues();
  279. $size = count($values);
  280. $this->beginRpc($x);
  281. $this->_return();
  282. // return [a,b,c];
  283. $this->lbracket();
  284. for ($i=0; $i<$size; $i++) {
  285. $this->accept($values[$i]);
  286. if ($i < $size - 1) {
  287. $this->comma();
  288. }
  289. }
  290. $this->rbracket();
  291. $this->semi();
  292. $this->commit($x);
  293. return false;
  294. }
  295. public function visitSet(SetCommand $x, Context $ctx) {
  296. $fieldName = $this->clientOracle->getFieldId($x->getFieldDeclClass(), $x->getField());
  297. if (is_null($fieldName)) {
  298. throw new IncompatibleRemoteServiceException('The client does not have field ' . $x->getField() . ' in type ' . $x->getFieldDeclClass().getFullName());
  299. }
  300. // i[3].foo = bar
  301. $this->push($this->parent->makeBackRef(array_peek($this->stack)));
  302. $this->dot();
  303. $this->push($fieldName);
  304. $this->eq();
  305. $this->accept($x->getValue());
  306. return false;
  307. }
  308. public function visitThrow(ThrowCommand $x, Context $ctx) {
  309. // throw foo;
  310. $this->beginRpc($x);
  311. $this->_throw();
  312. assert(count($x->getValues()) == 1);
  313. $this->acceptArray($x->getValues());
  314. $this->semi();
  315. $this->commit($x);
  316. return false;
  317. }
  318. private function _new() {
  319. $this->push(WebModePayloadSink::NEW_BYTES);
  320. }
  321. private function _null() {
  322. $this->push(WebModePayloadSink::NULL_BYTES);
  323. }
  324. private function _return() {
  325. $this->push(WebModePayloadSink::RETURN_BYTES);
  326. }
  327. private function _throw() {
  328. $this->push(WebModePayloadSink::THROW_BYTES);
  329. }
  330. private function beginRpc(RpcCommand $x) {
  331. assert(!$this->commandBuffers->containsKey($x));
  332. $this->started->add($x);
  333. array_push($this->stack, $x);
  334. $this->currentBuffer = new StringBuffer();
  335. $this->commandBuffers->put($x, $this->currentBuffer);
  336. }
  337. private function beginValue(ValueCommand $x) {
  338. $this->beginRpc($x);
  339. return $this->parent->makeBackRef($x);
  340. }
  341. private function comma() {
  342. $this->push(WebModePayloadSink::COMMA_BYTES);
  343. $this->spaceOpt();
  344. }
  345. private function commit(RpcCommand $x, $send = true) {
  346. if (array_pop($this->stack) !== $x) {
  347. throw new IllegalStateException('Did not pop expected command');
  348. }
  349. $x->clear();
  350. $sb = $this->commandBuffers->remove($x);
  351. assert(!is_null($sb));
  352. if (!empty($this->stack)) {
  353. $this->currentBuffer = $this->commandBuffers->get(array_peek($this->stack));
  354. assert(!is_null($this->currentBuffer));
  355. }
  356. else {
  357. $this->currentBuffer = null;
  358. }
  359. if ($send) {
  360. try {
  361. $this->parent->send($sb);
  362. }
  363. catch (SerializationException $e) {
  364. error_log($e->getMessage());
  365. exit(-1);
  366. }
  367. }
  368. else {
  369. $this->pushBuffer($sb);
  370. }
  371. }
  372. private function constructorFunctionArray(ArrayValueCommand $x) {
  373. $targetClass = ArrayType::clazz($x->getComponentType());
  374. $functionName = $this->constructorFunctions->get($targetClass);
  375. if (!is_null($functionName)) {
  376. return $functionName;
  377. }
  378. $initValuesId = $this->clientOracle->getMethodIdByClassName(
  379. 'com.google.gwt.lang.Array', 'initValues',
  380. array('Ljava/lang/Class;', 'Lcom/google/gwt/core/client/JavaScriptObject;', 'I', 'Lcom/google/gwt/lang/Array;'));
  381. assert(!is_null($initValuesId));
  382. $classLitId = $this->clientOracle->getFieldIdByClassName(
  383. 'com.google.gwt.lang.ClassLiteralHolder', $this->getJavahSignatureName($x->getComponentType()) . '_classLit');
  384. assert(!is_null($classLitId));
  385. $functionName = $this->clientOracle->createUnusedIdent($classLitId);
  386. $this->constructorFunctions->put($targetClass, $functionName);
  387. $castableTypeData = $this->clientOracle->getCastableTypeData($targetClass);
  388. if (is_null($castableTypeData)) {
  389. $castableTypeData = $this->clientOracle->getCastableTypeData(Classes::classOf('Object[]'));
  390. }
  391. $queryId = $this->clientOracle->getQueryId($x->getComponentType());
  392. if ($queryId == 0) {
  393. $queryId = $this->clientOracle->getQueryId(Classes::classOf('Object'));
  394. }
  395. $ident = '_0';
  396. // function foo(_0) {return initValues(classLid, castableTypeData, queryId, _0)}
  397. $this->_function();
  398. $this->push($functionName);
  399. $this->lparen();
  400. $this->push($ident);
  401. $this->rparen();
  402. $this->lbrace();
  403. $this->_return();
  404. $this->push($initValuesId);
  405. $this->lparen();
  406. $this->push($classLitId);
  407. $this->comma();
  408. $this->push($castableTypeData->toJs());
  409. $this->comma();
  410. $this->push(String::valueOf($queryId));
  411. $this->comma();
  412. $this->push($ident);
  413. $this->rparen();
  414. $this->rbrace();
  415. $this->flush($x);
  416. return $functionName;
  417. }
  418. private function constructorFunctionInstantiate(InstantiateCommand $x) {
  419. $targetClass = $x->getTargetClass();
  420. $functionName = $this->constructorFunctions->get($targetClass);
  421. if (!is_null($functionName)) {
  422. return $functionName;
  423. }
  424. //echo '[constructor ' . $targetClass->getFullName() . ']';
  425. $seedName = $this->clientOracle->getSeedName($targetClass);
  426. assert(!is_null($seedName));
  427. $functionName = $this->clientOracle->createUnusedIdent($seedName);
  428. $this->constructorFunctions->put($targetClass, $functionName);
  429. $setters = $x->getSetters();
  430. $idents = array_new(count($setters) + 1, '');
  431. for ($i=0, $j=count($idents); $i < $j; $i++) {
  432. $idents[$i] = '_' . $i;
  433. }
  434. // function foo(_0, _1, _2) {_0.a = _1; _0.b=_2; return _0}
  435. $this->_function();
  436. $this->push($functionName);
  437. $this->lparen();
  438. for ($i=0, $j=count($idents); $i<$j; $i++) {
  439. $this->push($idents[$i]);
  440. if ($i < $j - 1) {
  441. $this->comma();
  442. }
  443. }
  444. $this->rparen();
  445. $this->lbrace();
  446. $this->newlineOpt();
  447. for ($i=1, $j=count($idents); $i<$j; $i++) {
  448. $setter = $setters[$i - 1];
  449. $fieldIdent = $this->clientOracle->getFieldId($setter->getFieldDeclClass(), $setter->getField());
  450. // _0.foo = bar;
  451. $this->spaceOpt();
  452. $this->push($idents[0]);
  453. $this->dot();
  454. $this->push($fieldIdent);
  455. $this->eq();
  456. $this->push($idents[$i]);
  457. $this->semi();
  458. }
  459. $this->spaceOpt();
  460. $this->_return();
  461. $this->push($idents[0]);
  462. $this->rbrace();
  463. $this->newlineOpt();
  464. $this->flush($x);
  465. return $functionName;
  466. }
  467. private function dot() {
  468. $this->push(WebModePayloadSink::DOT_BYTES);
  469. }
  470. private function eq() {
  471. $this->spaceOpt();
  472. $this->push(WebModePayloadSink::EQ_BYTES);
  473. $this->spaceOpt();
  474. }
  475. private function flush(RpcCommand $x) {
  476. $sb = $this->commandBuffers->get($x);
  477. if (is_null($sb) || ($sb->getPosition() == 0)) {
  478. return;
  479. }
  480. try {
  481. $this->parent->send($sb);
  482. }
  483. catch (SerializationException $e) {
  484. error_log($e->getMessage());
  485. exit(-1);
  486. }
  487. $sb->clear();
  488. }
  489. private function _function() {
  490. $this->newlineOpt();
  491. $this->push(WebModePayloadSink::FUNCTION_BYTES);
  492. }
  493. private function getJavahSignatureName(Clazz $clazz) {
  494. if ($clazz->isArray()) {
  495. $leafType = $clazz;
  496. $dims = 0;
  497. do {
  498. $dims++;
  499. $leafType = $leafType->getComponentType();
  500. } while (!is_null($leafType->getComponentType()));
  501. assert($dims > 0);
  502. $s = $this->getJavahSignatureName($leafType);
  503. for ($i=0; $i < $dims; ++$i) {
  504. $s = '_3' . $s;
  505. }
  506. return $s;
  507. }
  508. else if ($clazz->isPrimitive()) {
  509. return WebModeClientOracle::jsniName($clazz);
  510. }
  511. else {
  512. $name = $clazz->getFullName();
  513. $name = str_replace('_', '_1', $name);
  514. $name = str_replace('.', '_', $name);
  515. return 'L' . $name . '_2';
  516. }
  517. }
  518. private function isStarted(RpcCommand $x) {
  519. return $this->started->contains($x);
  520. }
  521. private function lbrace() {
  522. $this->push(WebModePayloadSink::LBRACE_BYTES);
  523. }
  524. private function lbracket() {
  525. $this->push(WebModePayloadSink::LBRACKET_BYTES);
  526. }
  527. private function lparen() {
  528. $this->push(WebModePayloadSink::LPAREN_BYTES);
  529. }
  530. private function newlineOpt() {
  531. $this->pushOpt(WebModePayloadSink::NEWLINE_BYTES);
  532. }
  533. private function one() {
  534. $this->push(WebModePayloadSink::ONE_BYTES);
  535. }
  536. private function push($data) {
  537. $this->currentBuffer->put($data);
  538. }
  539. private function pushBuffer(StringBuffer $buffer) {
  540. assert(!is_null($this->currentBuffer));
  541. $this->currentBuffer->put($buffer);
  542. }
  543. private function pushOpt($x) {
  544. if (WebModePayloadSink::PRETTY) {
  545. $this->push($x);
  546. }
  547. }
  548. private function quote() {
  549. $this->push(WebModePayloadSink::QUOTE_BYTES);
  550. }
  551. private function rbrace() {
  552. $this->push(WebModePayloadSink::RBRACE_BYTES);
  553. }
  554. private function rbracket() {
  555. $this->push(WebModePayloadSink::RBRACKET_BYTES);
  556. }
  557. private function rparen() {
  558. $this->push(WebModePayloadSink::RPAREN_BYTES);
  559. }
  560. private function semi() {
  561. $this->push(WebModePayloadSink::SEMI_BYTES);
  562. $this->newlineOpt();
  563. }
  564. private function spaceOpt() {
  565. $this->pushOpt(WebModePayloadSink::SPACE_BYTES);
  566. }
  567. private function zero() {
  568. $this->push(WebModePayloadSink::ZERO_BYTES);
  569. }
  570. }
  571. class WebModePayloadSink extends CommandSink {
  572. const PRETTY = false;
  573. const COMMA_BYTES = ",";
  574. const DOT_BYTES = ".";
  575. const EQ_BYTES = "=";
  576. const FUNCTION_BYTES = "function ";
  577. const LBRACE_BYTES = "{";
  578. const LBRACKET_BYTES = "[";
  579. const LPAREN_BYTES = "(";
  580. const NEW_BYTES = "new ";
  581. const NEWLINE_BYTES = "\n";
  582. const NULL_BYTES = "null";
  583. const ONE_BYTES = "1";
  584. const QUOTE_BYTES = "\"";
  585. const RBRACE_BYTES = "}";
  586. const RBRACKET_BYTES = "]";
  587. const RETURN_BYTES = "return ";
  588. const RPAREN_BYTES = ")";
  589. const SPACE_BYTES = " ";
  590. const SEMI_BYTES = ";";
  591. const THROW_BYTES = "throw ";
  592. const ZERO_BYTES = "0";
  593. private $clientOracle;
  594. private $finished = false;
  595. private $out;
  596. private $valueBackRefs;
  597. private $visitor;
  598. private $freeBackRefs = array();
  599. public function __construct(ClientOracle $clientOracle, OutputStream $out) {
  600. $this->valueBackRefs = new ObjectMap();
  601. $this->clientOracle = $clientOracle;
  602. $this->out = $out;
  603. $this->visitor = new WebModePayloadVisitor($this);
  604. }
  605. public function accept(RpcCommand $command) {
  606. if ($this->finished) {
  607. throw new IllegalStateException('finish() has already been called');
  608. }
  609. $back = new BackRefAssigner($this);
  610. $back->accept($command);
  611. if ($command instanceof ValueCommand) {
  612. $this->makeBackRef($command);
  613. }
  614. $this->visitor->accept($command);
  615. }
  616. public function finish() {
  617. $this->finished = true;
  618. }
  619. public function getClientOracle() {
  620. return $this->clientOracle;
  621. }
  622. public function forget(ValueCommand $x) {
  623. assert($this->valueBackRefs->containsKey($x));
  624. array_push($this->freeBackRefs, $this->valueBackRefs->remove($x));
  625. }
  626. public function hasBackRef(ValueCommand $x) {
  627. return $this->valueBackRefs->containsKey($x);
  628. }
  629. public function makeBackRef(ValueCommand $x) {
  630. $toReturn = $this->valueBackRefs->get($x);
  631. if (is_null($toReturn)) {
  632. if (empty($this->freeBackRefs)) {
  633. $idx = $this->valueBackRefs->size();
  634. $toReturn = CommandClientSerializationStreamReader::BACKREF_IDENT . '._' .
  635. Integer::toString($idx, Character::MAX_RADIX);
  636. }
  637. else {
  638. $toReturn = array_pop($this->freeBackRefs);
  639. }
  640. $this->valueBackRefs->put($x, $toReturn);
  641. }
  642. return $toReturn;
  643. }
  644. public function send(StringBuffer $x) {
  645. $this->out->write((string) $x);
  646. }
  647. }
  648. /** @gwtname com.google.gwt.rpc.client.impl.CommandClientSerializationStreamReader */
  649. class CommandClientSerializationStreamReader {
  650. const BACKREF_IDENT = '_';
  651. }