PageRenderTime 49ms CodeModel.GetById 18ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

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