This is an automated email from the ASF dual-hosted git repository. gregdove pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
commit db0995ea20121a7e0490627592367473248266f9 Author: greg-dove <greg.d...@gmail.com> AuthorDate: Thu May 28 16:36:51 2020 +1200 AMF improvements: Coverage for ignoring custom namespaces and also cases where Transient fields are still deserialized. They are only skipped during local serialization. --- .../royale/net/remoting/amf/AMFBinaryData.as | 41 +++++--- .../network/AMFBinaryDataTesterTest.as | 109 ++++++++++++++++++++- .../flexUnitTests/network/support/TestClass6.as | 72 ++++++++++++++ .../flexUnitTests/network/support/TestClass7a.as | 51 ++++++++++ .../flexUnitTests/network/support/TestClass7b.as | 49 +++++++++ .../flexUnitTests/network/support/testnamespace.as | 26 +++++ 6 files changed, 334 insertions(+), 14 deletions(-) diff --git a/frameworks/projects/Network/src/main/royale/org/apache/royale/net/remoting/amf/AMFBinaryData.as b/frameworks/projects/Network/src/main/royale/org/apache/royale/net/remoting/amf/AMFBinaryData.as index 3eb6955..89350f4 100644 --- a/frameworks/projects/Network/src/main/royale/org/apache/royale/net/remoting/amf/AMFBinaryData.as +++ b/frameworks/projects/Network/src/main/royale/org/apache/royale/net/remoting/amf/AMFBinaryData.as @@ -499,6 +499,7 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu var l:uint; var metas:Array; var exclude:Boolean; + var transient:Boolean; var fieldName:String; const into:Array = localTraits.props; @@ -509,9 +510,11 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu if (fieldName.indexOf('::') != -1) continue; var field:Object = fieldSet[fieldName]; exclude = false; + transient = false; + var alreadyPresent:Boolean = into.indexOf(fieldName) != -1 ; if (asAccessors) { exclude = field.access != 'readwrite'; - if (exclude && into.indexOf(fieldName) == -1) { //<-- if at some level we already have read-write access, then that wins + if (exclude && !alreadyPresent) { //<-- if at some level we already have read-write access, then that wins //check: does it combine to provide 'readwrite' permissions via accessChecks through inheritance chain if (accessChecks[fieldName] && accessChecks[fieldName] != field.access) { //readonly or writeonly overridde at one level and different at another == readwrite @@ -524,21 +527,18 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu } } } - if (!exclude && excludeTransient && field.metadata != null) { - //exclude anything marked as Transient + //if a subclass override does not redeclare the field as transient, then it is already considered explicitly 'non-transient' + if (!exclude && !alreadyPresent && excludeTransient && field.metadata != null) { + //we need to mark Transient fields as special case metas = field.metadata(); l = metas.length; while (l--) { if (metas[l].name == 'Transient') { - exclude = true; + transient = true; + Traits.markTransient(fieldName, localTraits); + break; } } - if (exclude && into.indexOf(fieldName) != -1) { - //?possible case where it is marked transient on an ancestor but not in a subclass override - //it will not have been excluded when processing the subclass, which occurs first, so remove it now - //@todo untested : check this scenario, assume it should be removed - into.splice(into.indexOf(fieldName), 1); - } } if (!exclude) { //set up null/undefined value lookups for undefined field values (when encoding) @@ -554,7 +554,10 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu } else { nullValues[fieldName] = null; } - into.push(fieldName); + if (alreadyPresent) { + into.splice(into.indexOf(fieldName), 1); + } + if (!transient) into.push(fieldName); if (asAccessors) { localTraits.getterSetters[fieldName] = Traits.createInstanceAccessorGetterSetter(fieldName); } else { @@ -610,6 +613,9 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu classInfo = c.constructor.superClass_.ROYALE_CLASS_INFO; c = c.constructor.superClass_; } + //sometimes flash native seriazliation double-counts props and outputs some props data twice. + //this can happen with overrides (it was noticed with Transient overrides) + //it may mean that js amf output can sometimes be more compact, but should always deserialize to the same result. localTraits.count = localTraits.props.length; //not required, but useful when testing: localTraits.props.sort(); @@ -632,7 +638,6 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu } //not required, but useful when testing: localTraits.props.sort(); - } return localTraits; } @@ -1102,7 +1107,7 @@ class SerializationContext extends BinaryData implements IDataInput, IDataOutpu for (var i:uint = 0; i < l; i++) { var fieldValue:* = readObject(); var prop:String = decodedTraits.props[i]; - hasProp = localTraits && (localTraits.hasProp(prop) || localTraits.isDynamic); + hasProp = localTraits && (localTraits.hasProp(prop) || localTraits.isDynamic || localTraits.isTransient(prop)); if (hasProp) { localTraits.getterSetters[prop].setValue(obj, fieldValue); } else { @@ -1319,6 +1324,11 @@ class Traits { } }; } + + public static function markTransient(fieldName:String, traits:Traits):void{ + if (!traits.transients) traits.transients={}; + traits.transients[fieldName] = true; + } private static var _emtpy_object:Traits; @@ -1362,10 +1372,15 @@ class Traits { public var nullValues:Object = {}; public var getterSetters:Object = {}; + public var transients:Object ; public function hasProp(prop:String):Boolean { return props.indexOf(prop) != -1; } + + public function isTransient(prop:String):Boolean { + return transients && prop in transients; + } public function toString():String { if (goog.DEBUG) { diff --git a/frameworks/projects/Network/src/test/royale/flexUnitTests/network/AMFBinaryDataTesterTest.as b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/AMFBinaryDataTesterTest.as index 144d034..1cca14c 100644 --- a/frameworks/projects/Network/src/test/royale/flexUnitTests/network/AMFBinaryDataTesterTest.as +++ b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/AMFBinaryDataTesterTest.as @@ -27,7 +27,9 @@ package flexUnitTests.network import flexUnitTests.network.support.TestClass3; import flexUnitTests.network.support.TestClass4; import flexUnitTests.network.support.DynamicTestClass; - + import flexUnitTests.network.support.TestClass6; + import flexUnitTests.network.support.TestClass7a; + import flexUnitTests.network.support.TestClass7b; import org.apache.royale.test.asserts.*; import org.apache.royale.net.remoting.amf.AMFBinaryData; @@ -532,6 +534,111 @@ package flexUnitTests.network //javascript toXMLString pretty printing does not match exactly flash... assertTrue( xml.toXMLString() === xml2.toXMLString(), "XML round-tripping failed"); } + + + [Test] + public function testWithCustomNS():void + { + var ba:AMFBinaryData = new AMFBinaryData(); + var test:TestClass6 = new TestClass6(); + ba.writeObject(test); + ba.position = 0; + assertEquals(ba.length, 50, 'unexpected serialized content with custom namespaces'); + //cover variation in order + const validOptions:Array = [ + '0a23010b6d79566172156d794163636573736f7206177075626c69634d79566172061f7075626c6963206163636573736f72', + '0a2301156d794163636573736f720b6d79566172061f7075626c6963206163636573736f7206177075626c69634d79566172' + ]; + + assertTrue(validOptions.indexOf(getBytesOut(ba)) != -1, 'unexpected byte content with custom namespace content'); + + /* var restored:Object = ba.readObject(); + + var json:String = JSON.stringify(restored)*/ + //order may be different... need json object check here for: {"myAccessor":"public accessor","myVar":"publicMyVar"} + } + + [Test] + public function testTransientAndBindable():void{ + var ba:AMFBinaryData = new AMFBinaryData(); + registerClassAlias('TestClass7a', TestClass7a); + registerClassAlias('TestClass7b', TestClass7b); + var test1:TestClass7a = new TestClass7a(); + test1.something = 'whatever'; + ba.writeObject(test1); + + ba.position = 0; + + var retrieved:Object = ba.readObject(); + + assertTrue(retrieved is TestClass7a, 'unexpected deserialization'); + + var test2:TestClass7b = new TestClass7b(); + test1.something = 'whatever'; + ba.length = 0; + ba.writeObject(test2); + + ba.position = 0; + retrieved = ba.readObject(); + + assertTrue(retrieved is TestClass7b, 'unexpected deserialization'); + + } + + + [Test] + public function testTransientDeserialized():void{ + //if a transient field is already serialized, then we do deserialize it + //this means it can be sent from the server (for example) but is not sent to the server + + //to test this we will use TestClass7b (where 'something' is not Transient) which will serialize the 'something' field with an alias that matches + //a subsequent deserialization to a TestClass7a instance (where 'something' is Transient); + //the expected result will be that the Transient field is still deserialized + + var ba:AMFBinaryData = new AMFBinaryData(); + registerClassAlias('TestClass7a', TestClass7b); + var test1:TestClass7b = new TestClass7b(); + test1.something = 'something interesting'; + ba.length = 0; + //non-transient inbound: + ba.writeObject(test1); + + //The bytes for the above can differ between swf and js. 54 vs. 57 bytes length + //It is possible to correct that. But it is the swf output that is serializing the 'something' field twice + // (second time is string ref for both field and value, so quite compact, but still unnecessary) + //so not trying to match that exactly, because it is redundant data. + + ba.position = 0; + + //read it back as TestClass7a + registerClassAlias('TestClass7a', TestClass7a); + + var retrieved:Object = ba.readObject(); + + + assertTrue(retrieved is TestClass7a, 'unexpected deserialization'); + assertEquals(retrieved.something , 'something interesting', 'unexpected deserialization'); + assertEquals(retrieved.test , 'test', 'unexpected deserialization'); + + //this time round it won't be read out because it is Transient for serialization/inbound: + ba.position = 0; + ba.length=0; + ba.writeObject(retrieved); + ba.position = 0; + retrieved = ba.readObject(); + assertTrue(retrieved is TestClass7a, 'unexpected deserialization'); + assertEquals(retrieved.something , null, 'unexpected deserialization'); + assertEquals(retrieved.test , 'test', 'unexpected deserialization'); + } + + + private function getBytesOut(bytes:AMFBinaryData):String{ + var out:Array = []; + for each(var byte:uint in bytes) { + out.push(('0'+byte.toString(16)).substr(-2)); + } + return out.join(''); + } } } diff --git a/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass6.as b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass6.as new file mode 100644 index 0000000..42a1e3c --- /dev/null +++ b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass6.as @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package flexUnitTests.network.support +{ + + import flexUnitTests.network.support.testnamespace; + + /** + * @royalesuppresspublicvarwarning + */ + public class TestClass6 + { + //Note: do not change this test class unless you change the related tests to + //support any changes that might appear when testing reflection into it + + public function TestClass6() + { + + } + + + public var myVar:String = 'publicMyVar'; + + testnamespace var myVar:String = 'testnamespaceMyVar'; + + public function myMethod():String{ + return 'public myMethod'; + } + + testnamespace function myMethod():String{ + return 'testnamespace myMethod'; + } + + private var _v:String = 'public accessor'; + public function get myAccessor():String{ + return _v; + } + + public function set myAccessor(value:String):void{ + _v = value; + } + + private var _v2:String = 'testnamespace accessor'; + testnamespace function get myAccessor():String{ + return _v2; + } + + testnamespace function set myAccessor(value:String):void{ + _v2 = value; + } + + + } + + +} diff --git a/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass7a.as b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass7a.as new file mode 100644 index 0000000..db6ec8b --- /dev/null +++ b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass7a.as @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package flexUnitTests.network.support +{ + + import flexUnitTests.network.support.testnamespace; + + /** + * @royalesuppresspublicvarwarning + */ + public class TestClass7a + { + //Note: do not change this test class unless you change the related tests to + //support any changes that might appear when testing reflection into it + + public function TestClass7a() + { + + } + + public var test:String = 'test'; + + protected var _something:String; + [Transient] + public function get something():String{ + return _something; + } + public function set something(value:String):void{ + _something = value; + } + + } + + +} diff --git a/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass7b.as b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass7b.as new file mode 100644 index 0000000..39b5f7d --- /dev/null +++ b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/TestClass7b.as @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package flexUnitTests.network.support +{ + + import flexUnitTests.network.support.testnamespace; + + /** + * @royalesuppresspublicvarwarning + */ + public class TestClass7b extends TestClass7a + { + //Note: do not change this test class unless you change the related tests to + //support any changes that might appear when testing reflection into it + + public function TestClass7b() + { + + } + + + [Bindable] + override public function get something():String{ + return _something; + } + override public function set something(value:String):void{ + _something = value; + } + + } + + +} diff --git a/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/testnamespace.as b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/testnamespace.as new file mode 100644 index 0000000..3a552b8 --- /dev/null +++ b/frameworks/projects/Network/src/test/royale/flexUnitTests/network/support/testnamespace.as @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// +package flexUnitTests.network.support +{ + + + + public namespace testnamespace = 'http://testnamespace.com/network'; + +}