This is an automated email from the ASF dual-hosted git repository. gregdove pushed a commit to branch amf_updates in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
commit a56de5b2b9a76833a26a63ce5ceacd25309a34e9 Author: greg-dove <[email protected]> AuthorDate: Tue Feb 26 22:59:45 2019 +1300 Fixes to reflection, restoring functonality to UnitTests App Addition of support for release build access to public vars via reflection Addition of reflection utility methods: isDynamicObject getDynamicFields Addition of CompilationData inspector for runtime awareness of compiletime settings used on the inspection target. --- .../apache/royale/reflection/AccessorDefinition.as | 55 +++++- .../apache/royale/reflection/CompilationData.as | 179 ++++++++++++++++++ .../royale/reflection/MemberDefinitionBase.as | 44 +++-- .../apache/royale/reflection/MethodDefinition.as | 12 +- .../royale/reflection/ParameterDefinition.as | 6 +- .../org/apache/royale/reflection/TypeDefinition.as | 44 +++-- .../apache/royale/reflection/VariableDefinition.as | 106 ++++++++++- .../apache/royale/reflection/getDynamicFields.as | 197 ++++++++++++++++++++ .../apache/royale/reflection/isDynamicObject.as | 80 ++++++++ manualtests/UnitTests/build.xml | 12 +- manualtests/UnitTests/pom.xml | 18 +- .../src/main/config/compile-app-config.xml | 1 + .../UnitTests/src/main/royale/MyInitialView.mxml | 16 +- .../main/royale/flexUnitTests/ReflectionTester.as | 5 +- .../flexUnitTests/core/BinaryDataTesterTest.as | 34 ++-- .../reflection/ReflectionTesterTest.as | 20 +- .../reflection/ReflectionTesterTestDynamic.as | 203 +++++++++++++++++++++ .../reflection/support/DynamicTestClass.as | 89 +++++++++ .../support/DynamicTestClass2.as} | 57 ++++-- .../support/DynamicTestClass3.as} | 34 ++-- .../flexUnitTests/reflection/support/TestClass1.as | 3 +- manualtests/UnitTests/testsview/index.html | 45 +++-- 22 files changed, 1109 insertions(+), 151 deletions(-) diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/AccessorDefinition.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/AccessorDefinition.as index d18f855..4a93d8b 100644 --- a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/AccessorDefinition.as +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/AccessorDefinition.as @@ -17,6 +17,15 @@ // //////////////////////////////////////////////////////////////////////////////// package org.apache.royale.reflection { + + COMPILE::JS{ + import goog.DEBUG; + } + + COMPILE::SWF{ + import flash.utils.getDefinitionByName; + } + /** * The description of a Class or Interface accessor (get and/or set) * @@ -27,8 +36,9 @@ package org.apache.royale.reflection { */ public class AccessorDefinition extends VariableDefinition { - public function AccessorDefinition(name:String, rawData:Object) { - super(name, rawData); + public function AccessorDefinition(name:String, isStatic:Boolean, owner:TypeDefinition, rawData:Object = null) + { + super(name,isStatic, owner, rawData); } /** * The type that defined this accessor @@ -42,7 +52,7 @@ package org.apache.royale.reflection { COMPILE::JS{ var declareBy:String = _rawData.declaredBy; } - return TypeDefinition.getDefinition(declareBy); + return TypeDefinition.internalGetDefinition(declareBy); } @@ -65,6 +75,45 @@ package org.apache.royale.reflection { return _access; } + + COMPILE::JS + override public function get getValue():Function{ + if (_getter != null) return _getter; + var cl:Class = getDefinitionByName(owner.qualifiedName) as Class; + var fieldName:String = name; + if (isStatic) { + _getter = function():* {return cl[fieldName]} + } else { + _getter = function(instance:Object):* { + if (goog.DEBUG) { + if (arguments.length != 1 || (!(instance is cl))) throw 'invalid getValue parameters'; + } + return instance[fieldName]; + } + } + return _getter; + } + + COMPILE::JS + override public function get setValue():Function{ + if (_setter != null) return _setter; + var cl:Class = getDefinitionByName(owner.qualifiedName) as Class; + var fieldName:String = name; + if (isStatic) { + _setter = function(value:*):* { + cl[fieldName] = value + } + } else { + _setter = function(instance:Object, value:*):* { + if (goog.DEBUG) { + if (arguments.length != 2 || (!(instance is cl))) throw 'invalidsetValue parameters'; + } + instance[fieldName] = value; + } + } + return _getter; + } + /** * A string representation of this accessor definition diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/CompilationData.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/CompilationData.as new file mode 100644 index 0000000..9d7ba42 --- /dev/null +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/CompilationData.as @@ -0,0 +1,179 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 org.apache.royale.reflection { + + COMPILE::JS{ + import goog.DEBUG; + } + + COMPILE::SWF{ + import flash.utils.getDefinitionByName; + } + + /** + * Information about compiletime settings used when compiling the target class or instance + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Royale 0.0 + */ + public class CompilationData { + COMPILE::JS{ + //mirror of JSGoogConfiguration getReflectionFlags + + public static const WITH_DEFAULT_INITIALIZERS:uint = 1; + public static const HAS_KEEP_AS3_METADATA:uint = 2; + public static const HAS_KEEP_CODE_WITH_METADATA:uint = 4; + public static const HAS_EXPORT_PUBLIC_SYMBOLS:uint = 8; + public static const EXPORT_PROTECTED_SYMBOLS:uint = 16; + + private static const _ALL:Array = [ + WITH_DEFAULT_INITIALIZERS, + HAS_KEEP_AS3_METADATA, + HAS_KEEP_CODE_WITH_METADATA, + HAS_EXPORT_PUBLIC_SYMBOLS, + EXPORT_PROTECTED_SYMBOLS + ]; + + private static const _DESCRIPTIONS:Object = { + '1': 'Compiled with default initializers enabled', + '2': 'Compiled with request to keep specific as3 metadata, if present', + '4': 'Compiled to avoid deadcode elimination if code has metadata', + '8': 'Compiled with public symbols export enabled', + '16': 'Compiled with protected symbols export enabled' + } + } + + + public static function hasCompilationOption(flags:uint, optionBits:uint):Boolean { + COMPILE::SWF{ + return false; + } + COMPILE::JS{ + return Boolean((flags & optionBits) === optionBits); + } + } + + public static function asStrings(flags:uint):Array { + var ret:Array = []; + COMPILE::JS{ + var source:Array = _ALL; + const l:uint = source.length; + for (var i:uint = 0; i < l; i++) { + var itemFlag:uint = source[i]; + if (Boolean(flags & itemFlag)) { + ret.push(_DESCRIPTIONS[itemFlag]); + } + } + } + return ret; + } + + COMPILE::SWF + public function CompilationData(inspect:Object) { + throw new Error('CompilationData not implemented for swf'); + } + + COMPILE::JS + /** + * @royaleignorecoercion Class + */ + public function CompilationData(inspect:Object) { + if (!inspect) throw new Error('CompilationData constructor parameter cannot be null'); + var constructor:Object = inspect.constructor; + + if (constructor === Object['constructor']) { + //class or interface + if (inspect.prototype && inspect.prototype.ROYALE_REFLECTION_INFO) { + _qName = inspect.prototype.ROYALE_CLASS_INFO.names[0].qName; + _flags = inspect.prototype.ROYALE_REFLECTION_INFO.compileFlags; + _class = inspect as Class; + } + } else { + //instance + if (inspect.ROYALE_REFLECTION_INFO) { + _flags = inspect.ROYALE_REFLECTION_INFO.compileFlags; + _qName = inspect.ROYALE_CLASS_INFO.names[0].qName; + _class = constructor as Class; + } + } + if (!_qName) { + throw new Error('This is not a Royale Class, cannot get compilation data'); + } + } + + private var _class:Class; + + /** + * Check the ancestry for consistent compilation settings. + * @param specificFlags a flag or flag combination to check for on ancestor classes. + * If not specified or less than zero, then it is the full set of flags on this + * CompilationData. + * @return true if the ancestors have the same flags set, false otherwise + */ + public function hasSameAncestry(specificFlags:int = -1):Boolean{ + + var checkFlags:uint = specificFlags > -1 ? specificFlags : _flags; + COMPILE::JS{ + //check that the prototype chain has the same compile flags + var proto:Object = _class.prototype; + while (proto && proto.ROYALE_REFLECTION_INFO) { + if (!hasCompilationOption(proto.ROYALE_REFLECTION_INFO.compileFlags, checkFlags)) { + return false; + } + proto = proto.constructor.superClass_; + } + } + return true; + } + + private var _qName:String; + + /** + * + */ + public function get qualifiedName():String{ + return _qName; + } + + private var _flags:uint; + /** + * the compilation flags applicable to the inspected item + */ + public function get flags():uint{ + return flags; + } + + + /** + * A string representation of this CompilationData definition + */ + public function toString():String { + var s:String = "CompilationData for: '" + _qName + "\n"; + var contents:Array = asStrings(_flags); + if (!contents.length) { + s += '[no compilation flags recognized]\n'; + } else { + s +=('[' + contents.join(',\n ') + ']\n'); + } + return s; + } + } +} diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MemberDefinitionBase.as similarity index 54% copy from manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as copy to frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MemberDefinitionBase.as index 74c5b4d..2878e26 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MemberDefinitionBase.as @@ -16,23 +16,37 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package flexUnitTests +package org.apache.royale.reflection { - import flexUnitTests.reflection.*; - - [Suite] - [RunWith("org.flexunit.runners.Suite")] - public class ReflectionTester - { - public function ReflectionTester() + + /** + * The base class for definition types that can be decorated with metadata in actionscript + * source code + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Royale 0.0 + */ + public class MemberDefinitionBase extends DefinitionWithMetaData + { + public function MemberDefinitionBase(name:String, isStatic:Boolean, owner:TypeDefinition, rawData:Object = null) { - // see notes in CoreTester - var arr:Array = [ReflectionTesterTest, ReflectionTesterTestUseCache, ReflectionTesterTestAlias]; + super(name, rawData); + _isStatic = isStatic; + _owner = owner; } - - public var reflectionTesterCacheTest:ReflectionTesterTestUseCache; - public var reflectionTesterTest:ReflectionTesterTest; - - public var reflectionTesterAliasTest:ReflectionTesterTestAlias; + + private var _isStatic:Boolean; + public function get isStatic():Boolean{ + return _isStatic; + } + + private var _owner:TypeDefinition; + public function get owner():TypeDefinition{ + return _owner; + } + + } } diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MethodDefinition.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MethodDefinition.as index 0f5eafc..1cf7259 100755 --- a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MethodDefinition.as +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/MethodDefinition.as @@ -21,17 +21,17 @@ package org.apache.royale.reflection /** * The description of a method inside a class or interface - * + * * @langversion 3.0 * @playerversion Flash 10.2 * @playerversion AIR 2.6 * @productversion Royale 0.0 */ - public class MethodDefinition extends DefinitionWithMetaData + public class MethodDefinition extends MemberDefinitionBase { - public function MethodDefinition(name:String, rawData:Object) + public function MethodDefinition(name:String, isStatic:Boolean, owner:TypeDefinition, rawData:Object = null) { - super(name, rawData); + super(name,isStatic, owner, rawData); } /** @@ -46,7 +46,7 @@ package org.apache.royale.reflection COMPILE::JS{ var declareBy:String = _rawData.declaredBy; } - return TypeDefinition.getDefinition(declareBy); + return TypeDefinition.internalGetDefinition(declareBy); } private var _parameters:Array; @@ -100,7 +100,7 @@ package org.apache.royale.reflection var returnType:String = _rawData.type; } - return TypeDefinition.getDefinition(returnType); + return TypeDefinition.internalGetDefinition(returnType); } /** diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/ParameterDefinition.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/ParameterDefinition.as index c9893f7..1a88e1c 100644 --- a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/ParameterDefinition.as +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/ParameterDefinition.as @@ -21,7 +21,7 @@ package org.apache.royale.reflection /** * The description of a Function parameter - * + * * @langversion 3.0 * @playerversion Flash 10.2S * @playerversion AIR 2.6 @@ -40,11 +40,11 @@ package org.apache.royale.reflection */ public function get type():TypeDefinition{ COMPILE::SWF { - return TypeDefinition.getDefinition(_rawData.@type); + return TypeDefinition.internalGetDefinition(_rawData.@type); } COMPILE::JS { - return TypeDefinition.getDefinition(_rawData.type); + return TypeDefinition.internalGetDefinition(_rawData.type); } } diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/TypeDefinition.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/TypeDefinition.as index dd64e86..44bab7c 100755 --- a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/TypeDefinition.as +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/TypeDefinition.as @@ -19,12 +19,12 @@ package org.apache.royale.reflection { COMPILE::SWF { - import flash.utils.describeType; + import flash.utils.describeType; } /** * The description of a Class or Interface - * + * * @langversion 3.0 * @playerversion Flash 10.2 * @playerversion AIR 2.6 @@ -110,7 +110,11 @@ COMPILE::SWF { */ public static function getDefinition(name:String, rawData:Object = null):TypeDefinition { if (rawData == null) return null; - return _cache ? (_cache[name] || new TypeDefinition(name, rawData)) : new TypeDefinition(name, rawData); + return internalGetDefinition(name, rawData); + } + + internal static function internalGetDefinition(name:String, rawData:Object = null):TypeDefinition{ + return _cache ? (_cache[name] || new TypeDefinition(name, rawData)) : new TypeDefinition(name, rawData); } /** @@ -224,7 +228,7 @@ COMPILE::SWF { if (_rawData == null) { if (_packageName.length) - def = getDefinitionByName(_packageName + "::" + _name); + def = getDefinitionByName(_packageName + "." + _name); else def = getDefinitionByName(_name); _rawData = flash.utils.describeType(def); @@ -259,10 +263,10 @@ COMPILE::SWF { if (source ==null) { //constructor with no params _constructorMethod = - new MethodDefinition(_name, XML('<method name="'+_name+'" declaredBy="'+declaredBy+'" returnType="" />')); + new MethodDefinition(_name, false, this, XML('<method name="'+_name+'" declaredBy="'+declaredBy+'" returnType="" />')); } else { var params:XMLList = source.parameter; - _constructorMethod=new MethodDefinition(_name, XML('<method name="'+_name+'" declaredBy="'+declaredBy+'" returnType="">'+params.toXMLString()+'</method>')) + _constructorMethod=new MethodDefinition(_name, false, this, XML('<method name="'+_name+'" declaredBy="'+declaredBy+'" returnType="">'+params.toXMLString()+'</method>')) } } } @@ -290,7 +294,7 @@ COMPILE::SWF { * For a "class" kind TypeDefinition, this returns the TypeDefinitions * of the base classes (inheritance chain). This may differ between * javascript and flash platform targets for some classes. - * @royaleignorecoercion XML + * @royaleignorecoercion XML */ public function get baseClasses():Array { @@ -322,7 +326,7 @@ COMPILE::SWF { { var item:XML = data[i] as XML; var qname:String = item.@type; - results.push(TypeDefinition.getDefinition(qname)); + results.push(TypeDefinition.internalGetDefinition(qname)); } } COMPILE::JS @@ -337,7 +341,7 @@ COMPILE::SWF { { if (superClass.ROYALE_CLASS_INFO !== undefined) { qname = superClass.ROYALE_CLASS_INFO.names[0].qName; - results.push(TypeDefinition.getDefinition(qname)); + results.push(TypeDefinition.internalGetDefinition(qname)); def = getDefinitionByName(qname); superClass = def.superClass_; //todo: support for when superClass is not a royale 'class' @@ -388,7 +392,7 @@ COMPILE::SWF { { var item:XML = data[i] as XML; var qname:String = item.@type; - results.push(TypeDefinition.getDefinition(qname)); + results.push(TypeDefinition.internalGetDefinition(qname)); } } COMPILE::JS @@ -433,7 +437,7 @@ COMPILE::SWF { _interfaces = results; results = results.slice(); } - return results; + return results; } @@ -566,7 +570,7 @@ COMPILE::SWF { _variables = results; results = results.slice(); } - return results; + return results; } private var _accessors:Array; @@ -606,7 +610,7 @@ COMPILE::SWF { _accessors = results; results = results.slice(); } - return results; + return results; } @@ -644,7 +648,7 @@ COMPILE::SWF { { results = getCollection("methods"); //special case, remove constructor method: - var i:int=0, l:int=results.length; + var i:uint=0, l:uint=results.length; for (;i<l;i++) { if (results[i].name==this.name) { //trace('found constructor '+results[i].toString()); @@ -658,7 +662,7 @@ COMPILE::SWF { _methods = results; results = results.slice(); } - return results; + return results; } @@ -680,7 +684,7 @@ COMPILE::SWF { { var item:XML = data[i] as XML; var qname:String = item.@name; - results[i]= new itemClass(qname, item); + results[i]= new itemClass(qname,isStatic, this, item); } return results; } @@ -714,7 +718,7 @@ COMPILE::SWF { else data = null; if (data) { - results = TypeDefinition.getDefinition(data.names[0].qName,data)[collection]; + results = TypeDefinition.getDefinition(data.names[0].qName, data)[collection]; l=results.length; for (i=0;i<l;i++) oldNames[i]=results[i].name; } else results=[]; @@ -730,7 +734,7 @@ COMPILE::SWF { var itemDef:Object = items[item]; if (isStatic) { //we are looking for static members only - if (item.charAt(0)=="|") results[i++] = new itemClass(item.substr(1), itemDef); + if (item.charAt(0)=="|") results[i++] = new itemClass(item.substr(1), true, this, itemDef); // if ( itemDef.isStatic) results[i++] = new itemClass(item, itemDef); } else { @@ -738,10 +742,10 @@ COMPILE::SWF { if (item.charAt(0)=="|") continue; //if (itemDef.isStatic) continue; //instance member: - var itemClassDef:DefinitionWithMetaData = new itemClass(item, itemDef); + var itemClassDef:MemberDefinitionBase = new itemClass(item, false, this, itemDef); if (resolve) { //resolve against older versions ("overrides") - var oldIdx:uint = oldNames.indexOf(itemClassDef.name); + var oldIdx:int = oldNames.indexOf(itemClassDef.name); if (oldIdx != -1) { //we have an override of an ancestor's definition, replace it results[oldIdx] = itemClassDef; diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/VariableDefinition.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/VariableDefinition.as index 36cf9e4..b6b89bb 100755 --- a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/VariableDefinition.as +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/VariableDefinition.as @@ -18,20 +18,26 @@ //////////////////////////////////////////////////////////////////////////////// package org.apache.royale.reflection { - + COMPILE::JS{ + import goog.DEBUG; + } + + COMPILE::SWF{ + import flash.utils.getDefinitionByName; + } /** * The description of a Class or Interface variable - * + * * @langversion 3.0 * @playerversion Flash 10.2 * @playerversion AIR 2.6 * @productversion Royale 0.0 */ - public class VariableDefinition extends DefinitionWithMetaData + public class VariableDefinition extends MemberDefinitionBase { - public function VariableDefinition(name:String, rawData:Object) + public function VariableDefinition(name:String, isStatic:Boolean, owner:TypeDefinition, rawData:Object = null) { - super(name, rawData); + super(name,isStatic, owner, rawData); } /** @@ -40,13 +46,99 @@ package org.apache.royale.reflection */ public function get type():TypeDefinition { COMPILE::SWF { - return TypeDefinition.getDefinition(_rawData.@type); + return TypeDefinition.internalGetDefinition(_rawData.@type); } COMPILE::JS { - return TypeDefinition.getDefinition(_rawData.type); + return TypeDefinition.internalGetDefinition(_rawData.type); } } + + protected var _getter:Function; + /** + * provides a function that supports reading the value described by this definition + * For instance member definitions it requires the instance to be passed as a single argument + * For static member definitions it requires no arguments + */ + public function get getValue():Function{ + if (_getter != null) return _getter; + COMPILE::SWF{ + var fieldName:String = this.name; + var cl:Class = flash.utils.getDefinitionByName(owner.qualifiedName) as Class; + if (isStatic) { + _getter = function():* {return cl[fieldName]} + } else { + _getter = function(instance:Object):* { + if (arguments.length != 1 || (!(instance is cl))) throw 'invalid getValue parameters'; + return instance[fieldName]; + } + } + } + COMPILE::JS { + var f:Function = _rawData.get_set; + var canBeUndefined:Boolean = type.qualifiedName == '*'; + if (isStatic) { + _getter = function():* {return canBeUndefined ? f(f) : f()} + } else { + _getter = function(instance:Object):* { + if (goog.DEBUG) { + if (arguments.length != 1 || !instance) throw 'invalid getValue parameters'; + } + return canBeUndefined ? f(instance, f) : f(instance) + } + } + } + return _getter; + } + + + protected var _setter:Function; + /** + * provides a function that supports setting the value described by this definition + * For static member definitions it requires only the value argument + * For instance member definitions it requires the instance to be passed as a first argument, followed by the value + */ + public function get setValue():Function{ + if (_setter != null) return _setter; + COMPILE::SWF{ + var fieldName:String = this.name; + var cl:Class = flash.utils.getDefinitionByName(owner.qualifiedName) as Class; + if (isStatic) { + _setter = function(value:*):* { + cl[fieldName] = value + } + } else { + _setter = function(instance:Object, value:*):* { + if (!(instance is cl)) throw 'invalid setValue parameters'; + instance[fieldName] = value; + } + } + } + + COMPILE::JS { + var f:Function = _rawData.get_set; + if (isStatic) { + _setter = function(value:*):* { + if (goog.DEBUG) { + if (arguments.length != 1) throw 'invalid setValue parameters'; + //todo: more robust runtime checking of value here for debug mode + } + f(value); + } + } else { + _setter = function(instance:Object, value:*):* { + if (goog.DEBUG) { + if (arguments.length != 2 || !instance) throw 'invalid setValue parameters'; + //todo: more robust runtime checking of value here for debug mode + } + f(instance, value); + } + } + } + return _setter; + } + + /** * A string representation of this variable definition */ diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/getDynamicFields.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/getDynamicFields.as new file mode 100644 index 0000000..3479ca9 --- /dev/null +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/getDynamicFields.as @@ -0,0 +1,197 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 org.apache.royale.reflection { + COMPILE::JS{ + import goog.DEBUG; + } + + import org.apache.royale.events.EventDispatcher; + + /** + * A utility method to check if an object is dynamic (i.e. can have non-sealed members added or deleted) + * Note that static class objects are always dynamic, as are (static) Interface Objects + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Royale 0.0 + * + * @param inspect the class or instance to check for dynamic fields + * @param includePredicate an optional function for which takes a single String argument + * and returns true to include that specific field in the results. If it returns false, + * that property is excluded. Defaults to null, which applies no filtering and returns all + * detected properties. + * @param checkFirst an optional check to verify that the inspect parameter is dynamic + * before inspecting it for dynamic properties. This should normally be left at its default value of true. + * If it is know that the object is dynamic before calling this method, setting this to false + * could improve performance in performance-sensitive code. + * The results of calling this method on non-dynamic objects may be less reliable or less consistent + * across target platforms if checkFirst is false + * + * @return the dynamic fields as Strings in an Array + */ + COMPILE::JS + public function getDynamicFields(inspect:Object, includePredicate:Function = null, checkFirst:Boolean = true):Array { + var arr:Array; + var i:uint = 0; + var assumeDynamic:Boolean = checkFirst ? isDynamicObject(inspect) : true; + var checkIncludes:Boolean = includePredicate != null; + var prop:String; + if (assumeDynamic) { + var constructor:Object = inspect.constructor; + if (constructor === Object['constructor']) { + //class or interface + //this relies on compiler-added support to prevent enumeration of some fields + //that are not protected from enumeration by default + //this is only added to classes that have static fields in the definition + arr = Object.keys(inspect); + if (inspect.prototype && inspect.prototype.ROYALE_REFLECTION_INFO) { + if (goog.DEBUG) { + if (!CompilationData.hasCompilationOption(inspect.prototype.ROYALE_REFLECTION_INFO.compileFlags, CompilationData.WITH_DEFAULT_INITIALIZERS)) { + trace('[WARN] getDynamicFields can be unreliable for static inspection of ' + inspect.prototype.ROYALE_CLASS_INFO.names[0].qName + ' because it was not compiled with \'js-default-initializers=true\''); + } + } + var avoidNames:Array = inspect.prototype.ROYALE_REFLECTION_INFO.statics; + if (avoidNames) { + var temp:Array = []; + var l:uint = arr.length; + for (i = 0; i < l; i++) { + prop = arr[i]; + if (avoidNames.indexOf(prop) == -1) { + temp.push(prop); + } + } + arr = temp; + } + } + } else { + var excludeFields:Array; + arr = Object.keys(inspect); + var warned:Boolean; + //the following is complicated, but appears to work in release mode + //if everything (including framework if it needs to be reflected into) is compiled with + //js-default-initializers=true + if (inspect.ROYALE_REFLECTION_INFO) { + const inspectReflect:Object = inspect.ROYALE_REFLECTION_INFO; + if (goog.DEBUG) { + if (!CompilationData.hasCompilationOption(inspectReflect.compileFlags, CompilationData.WITH_DEFAULT_INITIALIZERS)) { + trace('[WARN] getDynamicFields can be unreliable for ' + inspect.ROYALE_CLASS_INFO.names[0].qName + ' and any ancestor classes that were not compiled with \'js-default-initializers=true\''); + warned = true; + } + } + + var instanceExcludes:Array = inspectReflect.instanceExcludes; + if (!instanceExcludes) { + //special cases: + //this is not ideal, but seems necessary unless the properties were made not enumerable + //EventDispatcher + var eventDispatcherClassInfo:Object; + //todo - consider using reflection code instead of hard dependency on EventDispatcher + if (inspect is EventDispatcher) { + instanceExcludes = getDynamicFields['EventDispatcherFields']; + if (!instanceExcludes) { + var test:Object = new EventDispatcher(); + // + instanceExcludes = getDynamicFields['EventDispatcherFields'] = Object.keys(test); + } + const edc:Class = EventDispatcher; + eventDispatcherClassInfo = edc.prototype.ROYALE_CLASS_INFO; + } + + if (!instanceExcludes) instanceExcludes = []; + + var proto:Object = inspect.constructor.prototype; + while (proto) { + var protoReflect:Object = proto.ROYALE_REFLECTION_INFO; + if (goog.DEBUG && !warned) { + if (protoReflect && !CompilationData.hasCompilationOption(protoReflect.compileFlags, CompilationData.WITH_DEFAULT_INITIALIZERS)) { + //skip EventDispatcher because we already special-cased it + if (proto.ROYALE_CLASS_INFO.names[0].qName != eventDispatcherClassInfo.names[0].qName) { + trace('[WARN] getDynamicFields can be unreliable for ' + + inspect.ROYALE_CLASS_INFO.names[0].qName + + '\'s ancestor class ' + + proto.ROYALE_CLASS_INFO.names[0].qName + + ' and any of its ancestors that were not compiled with \'js-default-initializers=true\''); + warned = true; + } + } + } + var inherited:Array = proto.ROYALE_REFLECTION_INFO ? proto.ROYALE_REFLECTION_INFO.instanceExcludes : null; + var protoKeys:Array = inherited || Object.keys(proto); + l = protoKeys.length; + while (l--) { + var key:String = protoKeys[l]; + if (instanceExcludes.indexOf(key) == -1) instanceExcludes.push(key); + } + if (inherited) break; + proto = proto.constructor.superClass_; + } + + inspectReflect.instanceExcludes = instanceExcludes; + } + l = instanceExcludes.length; + if (l && !excludeFields) excludeFields = []; + while (l--) { + excludeFields.push(instanceExcludes[l]); + } + } + + if (excludeFields) { + l = excludeFields.length; + while (l--) { + var idx:int = arr.indexOf(excludeFields[l]); + if (idx != -1) { + arr.splice(idx, 1); + } + } + } + } + + if (checkIncludes) { + arr = arr.filter(includePredicate); + } + } else { + //it's not considered dynamic... + //so assume zero dynamic fields (even if technically in js + //there could actually be some at runtime) + arr = []; + } + + return arr; + } + + COMPILE::SWF + public function getDynamicFields(inspect:Object, includePredicate:Function = null, checkFirst:Boolean = true):Array { + + var i:uint = 0; + var assumeDynamic:Boolean = checkFirst ? isDynamicObject(inspect) : true; + var checkIncludes:Boolean = includePredicate != null; + var prop:String; + var arr:Array = []; + if (assumeDynamic) { + for (prop in inspect) { + if (!checkIncludes || includePredicate(prop)) + arr[i++] = prop; + } + } + + return arr; + } + +} diff --git a/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/isDynamicObject.as b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/isDynamicObject.as new file mode 100644 index 0000000..20aa884 --- /dev/null +++ b/frameworks/projects/Reflection/src/main/royale/org/apache/royale/reflection/isDynamicObject.as @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 org.apache.royale.reflection +{ +COMPILE::SWF +{ + import flash.utils.describeType; +} + + /** + * A utility method to check if an object is dynamic (can have non-sealed members added or deleted) + * Note that static class objects are always dynamic, as are Interface Objects + * + * @langversion 3.0 + * @playerversion Flash 10.2 + * @playerversion AIR 2.6 + * @productversion Royale 0.0 + */ + public function isDynamicObject(inspect:Object):Boolean + { + COMPILE::SWF + { + try + { + // this test for checking whether an object is dynamic or not is + // pretty hacky, but it assumes that no-one actually has a + // property defined called "wootHackwoot" + var o:* = inspect["wootHackwoot"]; + } + catch (e:Error) + { + // our object isn't an instance of a dynamic class + return false; + } + return true; + } + COMPILE::JS + { + if (!inspect) return false; + var constructor:Object = inspect.constructor; + if (constructor === Object['constructor']) { + //class or interface + return true; + } + if (constructor) { + //instance + if (constructor === Object || constructor === Array) return true; + var prototype:Object = constructor.prototype; + if (prototype && prototype.ROYALE_CLASS_INFO) { + return Boolean(prototype.ROYALE_CLASS_INFO.names[0].isDynamic); + } + var dyncheck:Boolean = false; + try { + + inspect["wootHackwoot"] = "wootHackwoot"; + if (inspect["wootHackwoot"] == "wootHackwoot") dyncheck=true; + delete inspect["wootHackwoot"]; + + } catch(e:Error) {}; + return dyncheck; + } else return true + } + } +} diff --git a/manualtests/UnitTests/build.xml b/manualtests/UnitTests/build.xml index 5712905..55b9a7d 100644 --- a/manualtests/UnitTests/build.xml +++ b/manualtests/UnitTests/build.xml @@ -19,7 +19,7 @@ --> -<project name="GenericTests" default="main" basedir="."> +<project name="UnitTests" default="main" basedir="."> <property name="ROYALE_HOME" location="../.."/> <property name="example" value="UnitTests" /> @@ -27,27 +27,27 @@ <property name="theme_arg" value="-keep-as3-metadata+=Test,BeforeClass,AfterClass,Before,After,TestVariance,Event,Bindable,TestMeta" /> <!-- adding flexuint style meta collection, and additional testing meta tags (Event,Bindable,TestMeta)--> <property name="extlib_arg" value="-keep-code-with-metadata=Test,BeforeClass,AfterClass,Before,After,TestVariance,TestMeta" /> - + <property file="${ROYALE_HOME}/env.properties"/> <property environment="env"/> <property file="${ROYALE_HOME}/build.properties"/> <property name="ROYALE_HOME" value="${ROYALE_HOME}"/> - + <!-- make ant src and output consistent with maven --> <property name="maven_compat" value="true"/> - + <include file="${basedir}/../build_example.xml" /> <target name="main" depends="clean,build_example.compile" description="Clean build of ${example}"> </target> - + <target name="clean"> <delete dir="${basedir}/bin" failonerror="false" /> <delete dir="${basedir}/bin-debug" failonerror="false" /> <delete dir="${basedir}/bin-release" failonerror="false" /> <delete dir="${basedir}/target" failonerror="false" /> </target> - + <target name="examine" depends="build_example.get.browser"> <echo message="View the swf and js unit tests side-by-side"/> <exec executable="${browser}" dir="${basedir}" failonerror="true"> diff --git a/manualtests/UnitTests/pom.xml b/manualtests/UnitTests/pom.xml index c887af5..b4e8f97 100644 --- a/manualtests/UnitTests/pom.xml +++ b/manualtests/UnitTests/pom.xml @@ -29,10 +29,10 @@ <version>0.9.6-SNAPSHOT</version> <packaging>swf</packaging> <name>Apache Royale: ManualTests: UnitTests</name> - - <properties> + + <properties> <title>${project.artifactId}</title> - <bgcolor>#ffffff</bgcolor> + <bgcolor>#ffffff</bgcolor> <useBrowserHistory>--</useBrowserHistory> <version_major>11</version_major> <version_minor>1</version_minor> @@ -56,7 +56,7 @@ <mainClass>${project.artifactId}.mxml</mainClass> <removeCirculars>true</removeCirculars> <debug>true</debug> - <additionalCompilerOptions>-keep-as3-metadata+=Test,BeforeClass,AfterClass,Before,After,TestVariance,Event,Bindable,TestMeta -keep-code-with-metadata=Test,BeforeClass,AfterClass,Before,After,TestVariance,TestMeta</additionalCompilerOptions> + <additionalCompilerOptions>-js-default-initializers;-keep-as3-metadata+=Test,BeforeClass,AfterClass,Before,After,TestVariance,Event,Bindable,TestMeta;-keep-code-with-metadata=Test,BeforeClass,AfterClass,Before,After,TestVariance,TestMeta</additionalCompilerOptions> <outputDirectory>${basedir}/target/bin-debug</outputDirectory> <flashOutputFileName>${project.artifactId}.swf</flashOutputFileName> </configuration> @@ -71,11 +71,11 @@ <targets>JSRoyale</targets> <mainClass>${project.artifactId}.mxml</mainClass> <debug>false</debug> - <additionalCompilerOptions>-keep-as3-metadata+=Test,BeforeClass,AfterClass,Before,After,TestVariance,Event,Bindable,TestMeta -keep-code-with-metadata=Test,BeforeClass,AfterClass,Before,After,TestVariance,TestMeta</additionalCompilerOptions> + <additionalCompilerOptions>-js-default-initializers;-keep-as3-metadata+=Test,BeforeClass,AfterClass,Before,After,TestVariance,Event,Bindable,TestMeta;-keep-code-with-metadata=Test,BeforeClass,AfterClass,Before,After,TestVariance,TestMeta</additionalCompilerOptions> <outputDirectory>${basedir}/target</outputDirectory> </configuration> </execution> - </executions> + </executions> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> @@ -92,8 +92,8 @@ <includeEmptyDirs>true</includeEmptyDirs> <resources> <resource> - <directory>${basedir}/../../templates/swfobject</directory> - <filtering>true</filtering> + <directory>${basedir}/src/main/resources/swfobject</directory> + <filtering>true</filtering> </resource> </resources> </configuration> @@ -135,7 +135,7 @@ <file>${basedir}/bin-debug/index.template.html</file> <regex>false</regex> <replacements> - + </replacements> </configuration> </execution> diff --git a/manualtests/UnitTests/src/main/config/compile-app-config.xml b/manualtests/UnitTests/src/main/config/compile-app-config.xml index 645a852..8774ac7 100644 --- a/manualtests/UnitTests/src/main/config/compile-app-config.xml +++ b/manualtests/UnitTests/src/main/config/compile-app-config.xml @@ -20,6 +20,7 @@ <js-output-optimization> <optimization>skipFunctionCoercions</optimization> </js-output-optimization> + <js-default-initializers>true</js-default-initializers> <compiler> </compiler> </royale-config> diff --git a/manualtests/UnitTests/src/main/royale/MyInitialView.mxml b/manualtests/UnitTests/src/main/royale/MyInitialView.mxml index 375981d..8f72540 100644 --- a/manualtests/UnitTests/src/main/royale/MyInitialView.mxml +++ b/manualtests/UnitTests/src/main/royale/MyInitialView.mxml @@ -159,18 +159,14 @@ limitations under the License. private static function getPlatform():String { - try { - var check:* = getDefinitionByName("flash.system.Capabilities"); - } catch (e:Error) { - return "JS"; + var platform:String; + COMPILE::JS{ + platform = "JS"; } - //if this next reference to 'check' is not included, then the above try/catch code - // appears to be optimized away in js-release mode - //[this issue was added to ObservedBugs Tests] - if (check == null) { - return "JS"; + COMPILE::SWF{ + platform = "SWF"; } - return "SWF"; + return platform; } public static const PLATFORM:String = getPlatform(); diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as index 74c5b4d..369e763 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as @@ -27,12 +27,15 @@ package flexUnitTests public function ReflectionTester() { // see notes in CoreTester - var arr:Array = [ReflectionTesterTest, ReflectionTesterTestUseCache, ReflectionTesterTestAlias]; + var arr:Array = [ReflectionTesterTest, ReflectionTesterTestUseCache, ReflectionTesterTestAlias, ReflectionTesterTestDynamic]; } public var reflectionTesterCacheTest:ReflectionTesterTestUseCache; public var reflectionTesterTest:ReflectionTesterTest; public var reflectionTesterAliasTest:ReflectionTesterTestAlias; + + + public var reflectionTesterDynamicTest:ReflectionTesterTestDynamic; } } diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/core/BinaryDataTesterTest.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/core/BinaryDataTesterTest.as index a2473f8..b6613b7 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/core/BinaryDataTesterTest.as +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/core/BinaryDataTesterTest.as @@ -25,7 +25,7 @@ package flexUnitTests.core import org.apache.royale.utils.BinaryData - public class BinaryDataTesterTest + public class BinaryDataTesterTest { [Before] @@ -524,20 +524,20 @@ package flexUnitTests.core [Test] - public function testWriteBytes():void + public function testWriteBinaryData():void { var ba:BinaryData = new BinaryData(); for (var i:int=0;i<50;i++) ba.writeByte(i); var newBa:BinaryData = new BinaryData(); - newBa.writeBytes(ba); + newBa.writeBinaryData(ba); - Assert.assertEquals("BinaryData writeBytes: length", 50, newBa.length); - Assert.assertEquals("BinaryData writeBytes: position", 50, newBa.position); + Assert.assertEquals("BinaryData writeBinaryData: length", 50, newBa.length); + Assert.assertEquals("BinaryData writeBinaryData: position", 50, newBa.position); for (i=0;i<50;i++) { - Assert.assertEquals("BinaryData writeBytes: content check", i, newBa.array[i]); + Assert.assertEquals("BinaryData writeBinaryData: content check", i, newBa.array[i]); } @@ -545,21 +545,29 @@ package flexUnitTests.core } [Test] - public function testReadBytes():void + public function testReadBinaryData():void { var ba:BinaryData = new BinaryData(); for (var i:int=0;i<50;i++) ba.writeByte(i); ba.position=0; var newBa:BinaryData = new BinaryData(); - ba.readBytes(newBa,5,10); - Assert.assertEquals("BinaryData readBytes: position", 10, ba.position); - Assert.assertEquals("BinaryData readBytes: length", 15, newBa.length); - Assert.assertEquals("BinaryData readBytes: position", 0, newBa.position); + ba.readBinaryData(newBa,5,10); + Assert.assertEquals("BinaryData readBinaryData: position", 10, ba.position); + Assert.assertEquals("BinaryData readBinaryData: length", 15, newBa.length); + Assert.assertEquals("BinaryData readBinaryData: position", 0, newBa.position); var expected:Array = [0,0,0,0,0,0,1,2,3,4,5,6,7,8,9]; - for (i=5;i<15;i++) { - Assert.assertEquals("BinaryData readBytes: content check", expected[i], newBa.array[i]); + for (i=0;i<15;i++) { + Assert.assertEquals("BinaryData readBinaryData: content check", expected[i], newBa.array[i]); } + newBa.position = 15; + ba.readBinaryData(newBa,5,10); + expected = [0,0,0,0,0,10,11,12,13,14,15,16,17,18,19]; + for (i=0;i<15;i++) { + Assert.assertEquals("BinaryData readBinaryData: content check", expected[i], newBa.array[i]); + } + Assert.assertEquals("BinaryData readBinaryData: length", 15, newBa.length); + Assert.assertEquals("BinaryData readBinaryData: position", 15, newBa.position); } [Test] diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTest.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTest.as index f4f0a01..7bd9bee 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTest.as +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTest.as @@ -23,7 +23,7 @@ package flexUnitTests.reflection import org.apache.royale.reflection.*; public class ReflectionTesterTest - { + { public static var isJS:Boolean; [BeforeClass] @@ -233,8 +233,12 @@ package flexUnitTests.reflection Assert.assertEquals("unexpected meta arg name","foo",metaArg.name); Assert.assertEquals("unexpected meta arg value","instanceVariable",metaArg.value); - Assert.assertEquals("unexpected reflection initial variable value","testVar_val",inst[variable.name]); - + // Assert.assertEquals("unexpected reflection initial variable value","testVar_val",inst[variable.name]); + Assert.assertEquals("unexpected reflection initial variable value","testVar_val",variable.getValue(inst)); + variable.setValue(inst, "testVar_val_reflection_set"); + Assert.assertEquals("unexpected reflection initial variable value","testVar_val_reflection_set",variable.getValue(inst)); + inst.testVar = "testVar_val"; + var accessors:Array = def.accessors; var testReadOnly:AccessorDefinition = retrieveItemWithName(accessors,"testReadOnly") as AccessorDefinition; meta = testReadOnly.retrieveMetaDataByName("TestMeta")[0]; @@ -296,8 +300,14 @@ package flexUnitTests.reflection Assert.assertEquals("unexpected meta arg name","foo",metaArg.name); Assert.assertEquals("unexpected meta arg value","staticVariable",metaArg.value); - Assert.assertEquals("unexpected reflection initial variable value","testStaticVar_val",TestClass2[variable.name]); - + // Assert.assertEquals("unexpected reflection initial variable value","testStaticVar_val",TestClass2[variable.name]); + Assert.assertEquals("unexpected reflection initial variable value","testStaticVar_val",variable.getValue()); + variable.setValue("testStaticVar_val_reflection_set"); + Assert.assertEquals("unexpected reflection initial variable value","testStaticVar_val_reflection_set",variable.getValue()); + TestClass2.testStaticVar = "testStaticVar_val"; + + + /** static accessors **/ accessors = def.staticAccessors; diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTestDynamic.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTestDynamic.as new file mode 100644 index 0000000..4aa2155 --- /dev/null +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/ReflectionTesterTestDynamic.as @@ -0,0 +1,203 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.reflection +{ + import flexunit.framework.Assert; + import flexUnitTests.reflection.support.*; + import org.apache.royale.reflection.*; + + public class ReflectionTesterTestDynamic + { + + public static var isJS:Boolean; + [BeforeClass] + public static function setUpBeforeClass():void + { + var js:Boolean; + COMPILE::JS{ + js = true; + } + isJS = js; + } + + [AfterClass] + public static function tearDownAfterClass():void + { + } + + [Before] + public function setUp():void + { + + } + + [After] + public function tearDown():void + { + + } + + private static function contentStringsMatch(arr1:Array, arr2:Array):Boolean{ + if (arr1 == arr2) return true; + if (!arr1 || !arr2) return false; + if (arr1.length != arr2.length) return false; + const arr1Copy:Array = arr1.concat(); + var l:uint = arr1Copy.length; + while (l--) { + var s:String = arr1Copy.shift(); + if (arr2.indexOf(s) == -1) return false + } + return true; + } + + + [Test] + public function testisDynamic():void { + //class + + Assert.assertTrue("class should be dynamic",isDynamicObject(Object)); + + Assert.assertTrue("class should be dynamic",isDynamicObject(TestClass1)); + //interface is dynamic (even if it doesn't make much sense) + Assert.assertTrue("interface should be dynamic",true,isDynamicObject(ITestInterface)); + //instance + Assert.assertTrue("generic object should be dynamic",isDynamicObject({})); + + Assert.assertFalse("sealed class instance should not be dynamic",isDynamicObject(new TestClass1())); + + Assert.assertTrue("dynamic class instance should be dynamic",isDynamicObject(new DynamicTestClass())); + + + Assert.assertFalse("String instance should not be dynamic",isDynamicObject("String")); + Assert.assertFalse("int instance should not be dynamic",isDynamicObject(99)); + Assert.assertFalse("Number instance should not be dynamic",isDynamicObject(99.99)); + Assert.assertFalse("Boolean instance should not be dynamic",isDynamicObject(true)); + + + Assert.assertTrue("function instance should be dynamic",isDynamicObject(function():void{})); + Assert.assertTrue("Array instance should be dynamic",isDynamicObject([])); + } + + + + [TestVariance(variance="JS",description="Variance in test due to reliance on 'js-default-initializers=true' throughout entire inheritance chain for 'getDynamicFields' function")] + [Test] + public function testGetDynamicFields():void { + + const emptyArray:Array = []; + const singleDynField:Array = ['test']; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(Object),emptyArray)); + Object['test'] = true; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(Object),singleDynField)); + delete Object['test']; + + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(TestClass1),emptyArray)); + TestClass1['test'] = true; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(TestClass1),singleDynField)); + delete TestClass1['test']; + + //interface is dynamic (even if it doesn't make much sense) + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(ITestInterface),emptyArray)); + ITestInterface['test'] = true; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(ITestInterface),singleDynField)); + delete ITestInterface['test']; + + //instance + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields({}),emptyArray)); + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields({test:true}),singleDynField)); + + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(new TestClass1()),emptyArray)); + + const dynInstance:DynamicTestClass = new DynamicTestClass(); + dynInstance.test = true; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(dynInstance),singleDynField)); + + + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields("String"),emptyArray)); + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(99),emptyArray)); + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(99.99),emptyArray)); + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(true),emptyArray)); + + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(function():void{}),emptyArray)); + + const numericFields:Array=["0","1","2","3"]; + var arr:Array = [1,2,3,4]; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(arr),numericFields)); + numericFields.push('test'); + arr['test'] = true; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(arr),numericFields)); + + + var testclass2:DynamicTestClass2 = new DynamicTestClass2(); + testclass2.test = true; + testclass2.something = '*something*'; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(testclass2),singleDynField)); + + + testclass2.test = 'test'; + testclass2.something = '*something else*'; + + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(testclass2),singleDynField)); + var testClass3:DynamicTestClass3 = new DynamicTestClass3(); + var swapAssertion:Boolean; + COMPILE::JS{ + var check:Boolean = new CompilationData(testClass3).hasSameAncestry(CompilationData.WITH_DEFAULT_INITIALIZERS); + if (!check) { + //variance... due framework ancestry having different compilation settings + swapAssertion = true; + } + } + + if (!swapAssertion) { + Assert.assertTrue("dynamic fields should match reference list", contentStringsMatch(getDynamicFields(testClass3), emptyArray)); + } else { + Assert.assertFalse("dynamic fields should match reference list", contentStringsMatch(getDynamicFields(testClass3), emptyArray)); + trace('[WARN] Variance: a test is technically wrong in javascript, but is expected to be wrong, because the compilation settings do not support it throughout the inheritance chain'); + } + testClass3.test='true'; + testClass3.x = 10; + testClass3.className = 'hello'; + + + if (!swapAssertion) { + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(testClass3),singleDynField)); + } else { + Assert.assertFalse("dynamic fields should not match reference list",contentStringsMatch(getDynamicFields(testClass3),singleDynField)); + trace('[WARN] Variance: a test is technically wrong in javascript, but is expected to be wrong, because the compilation settings do not support it throughout the inheritance chain'); + } + + } + + [Test] + public function testGetDynamicFieldsWithPredicate():void { + const test:Object = { + 'test1':true, + 'test2':true, + '_underscore':true + }; + const withoutUnderscores:Array = ['test1', 'test2']; + var excludeUnderscores:Function = function(prop:String):Boolean { + return prop && prop.charAt(0) != '_'; + }; + Assert.assertTrue("dynamic fields should match reference list",contentStringsMatch(getDynamicFields(test, excludeUnderscores), withoutUnderscores)); + + } + + } +} diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass.as new file mode 100644 index 0000000..c1062a5 --- /dev/null +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass.as @@ -0,0 +1,89 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.reflection.support +{ + + + + dynamic public class DynamicTestClass + { + //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 DynamicTestClass(){ + + }*/ + + + [TestMeta(foo="instanceMethod")] + public function method():void{ + + } + + [TestMeta(foo="instanceMethod")] + public function methodWithArgs(mandatory:String,optional:Boolean=true):void{ + _test = mandatory; + } + + [TestMeta(foo="instanceVariable")] + public var testVar:String; + + [TestMeta(foo="instanceAccessor")] + public function get testGetter():String{ + return null + } + + [TestMeta(foo="instanceAccessor")] + public function set testSetter(value:String):void{ + + } + + private var _test:String; + + [TestMeta(foo="staticMethod")] + public static function method():void{ + } + + [TestMeta(foo="staticMethod")] + public static function methodWithArgs(mandatory:String,optional:Boolean=true):void{ + } + + [TestMeta(foo="staticVariable")] + public static var testVar:String; + + [TestMeta(foo="staticAccessor")] + public static function get testGetter():String{ + return null + } + + [TestMeta(foo="staticAccessor")] + public static function set testSetter(value:String):void{ + + } + + private var private_var:Boolean; + + protected var protected_var:Boolean; + + private static var private_var:Boolean; + + protected static var protected_var:Boolean; + + } +} diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass2.as similarity index 52% copy from manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as copy to manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass2.as index 74c5b4d..2be6a15 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass2.as @@ -16,23 +16,44 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package flexUnitTests +package flexUnitTests.reflection.support { - import flexUnitTests.reflection.*; - - [Suite] - [RunWith("org.flexunit.runners.Suite")] - public class ReflectionTester - { - public function ReflectionTester() - { - // see notes in CoreTester - var arr:Array = [ReflectionTesterTest, ReflectionTesterTestUseCache, ReflectionTesterTestAlias]; - } - - public var reflectionTesterCacheTest:ReflectionTesterTestUseCache; - public var reflectionTesterTest:ReflectionTesterTest; - - public var reflectionTesterAliasTest:ReflectionTesterTestAlias; - } + + + + dynamic public class DynamicTestClass2 extends TestClass1 + { + //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 DynamicTestClass2(){ + //his.constructorDynTest = 'constructorDynTest'; + } + + public var something:String; + + [TestMeta(foo="staticMethod")] + public static function method():void{ + } + + [TestMeta(foo="staticMethod")] + public static function methodWithArgs(mandatory:String,optional:Boolean=true):void{ + } + + [TestMeta(foo="staticVariable")] + public static var testVar:String; + + [TestMeta(foo="staticAccessor")] + public static function get testGetter():String{ + return null + } + + [TestMeta(foo="staticAccessor")] + public static function set testSetter(value:String):void{ + + } + + + + } } diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass3.as similarity index 62% copy from manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as copy to manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass3.as index 74c5b4d..ce73e9d 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/ReflectionTester.as +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/DynamicTestClass3.as @@ -16,23 +16,21 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -package flexUnitTests +package flexUnitTests.reflection.support { - import flexUnitTests.reflection.*; - - [Suite] - [RunWith("org.flexunit.runners.Suite")] - public class ReflectionTester - { - public function ReflectionTester() - { - // see notes in CoreTester - var arr:Array = [ReflectionTesterTest, ReflectionTesterTestUseCache, ReflectionTesterTestAlias]; - } - - public var reflectionTesterCacheTest:ReflectionTesterTestUseCache; - public var reflectionTesterTest:ReflectionTesterTest; - - public var reflectionTesterAliasTest:ReflectionTesterTestAlias; - } + + import org.apache.royale.core.UIBase; + + dynamic public class DynamicTestClass3 extends UIBase + { + //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 DynamicTestClass3(){ + + + } + + + } } diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/TestClass1.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/TestClass1.as index 829d14f..8132890 100644 --- a/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/TestClass1.as +++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/reflection/support/TestClass1.as @@ -26,12 +26,13 @@ package flexUnitTests.reflection.support { public function TestClass1() { - var something:ITestInterface2; + // var something:ITestInterface2; } [Bindable] [Event(name="foo", type="org.apache.royale.events.Event")] public var bindableVar:String; + [Event(name="foo", type="org.apache.royale.events.Event")] public var temp:Boolean; diff --git a/manualtests/UnitTests/testsview/index.html b/manualtests/UnitTests/testsview/index.html index 6d41e89..2806402 100644 --- a/manualtests/UnitTests/testsview/index.html +++ b/manualtests/UnitTests/testsview/index.html @@ -20,20 +20,20 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <style> - .boxLeft { - display:inline-block; + .boxLeft { + display:inline-block; width:50% ; - position:absolute; - top:100px; - bottom:15px; + position:absolute; + top:100px; + bottom:15px; left:0px } - .boxRight { - display:inline-block; + .boxRight { + display:inline-block; width:50% ; - position:absolute; - top:100px; - bottom:15px; + position:absolute; + top:100px; + bottom:15px; right:0px } .pageStyles { @@ -66,7 +66,7 @@ } </style> <script type="text/javascript"> - + console.API; if (typeof console._commandLineAPI !== 'undefined') { @@ -94,18 +94,31 @@ case SWAP_TO_RELEASE: console.API.clear(); button.innerHTML = SWAP_TO_DEBUG; - jsIframe.src = release; - status.innerHTML = "Currently showing <em>"+JS_RELEASE+"</em>";; + jsIframe.src = release; + status.innerHTML = "Currently showing <em>"+JS_RELEASE+"</em>";; break; case SWAP_TO_DEBUG: console.API.clear(); button.innerHTML = SWAP_TO_RELEASE; - jsIframe.src = debug; - status.innerHTML = "Currently showing <em>"+JS_DEBUG+"</em>"; + jsIframe.src = debug; + status.innerHTML = "Currently showing <em>"+JS_DEBUG+"</em>"; break; } } } + + + function onSwfFrameReady(){ + //this supports intellij's 'launch in browser' + var swfFrameSource = "/target/bin-debug/UnitTests.html"; + if (window.location.search) { + var append = window.location.search; + if (append) swfFrameSource += append; + if ( document.getElementById('swfIframe').src.indexOf(swfFrameSource) == -1) { + document.getElementById('swfIframe').src =".."+swfFrameSource; + } + } + } </script> </head> <body class="pageStyles"> @@ -122,7 +135,7 @@ <iframe id="jsIframe" src="../target/javascript/bin/js-debug/index.html" frameborder="1" scrolling="no" height="100%" width="100%" align="left" ></iframe> </div> <div class="boxRight"> - <iframe id="swfIframe" src="../target/bin-debug/UnitTests.html" frameborder="1" scrolling="no" height="100%" width="100%" align="left" ></iframe> + <iframe id="swfIframe" onload="onSwfFrameReady()" frameborder="1" scrolling="no" height="100%" width="100%" align="left" ></iframe> </div> </div> </body>
