Chromium Code Reviews| Index: sdk/lib/js/dartium/js_dartium.dart |
| diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart |
| index e7f3004c686d8efbc0b5bdc038bbb28178f2f5d2..7f6494fa053137f6eafc592de558ee9caf3101be 100644 |
| --- a/sdk/lib/js/dartium/js_dartium.dart |
| +++ b/sdk/lib/js/dartium/js_dartium.dart |
| @@ -89,11 +89,383 @@ library dart.js; |
| import 'dart:collection' show ListMixin; |
| import 'dart:nativewrappers'; |
| +import 'dart:math' as math; |
| +import 'dart:mirrors' as mirrors; |
| + |
| +// TODO(jacobr): if we care about unchecked mode in Dartium we need to set this |
| +// to false in unchecked mode. |
| +final bool _CHECK_JS_INVOCATIONS = true; |
| + |
| +final _allowedMethods = new Map<Symbol, _DeclarationSet>(); |
| +final _allowedGetters = new Map<Symbol, _DeclarationSet>(); |
| +final _allowedSetters = new Map<Symbol, _DeclarationSet>(); |
| + |
| +final Set<Type> _jsInterfaceTypes = new Set<Type>(); |
| +Iterable<Type> get jsInterfaceTypes => _jsInterfaceTypes.toList(); |
| + |
| +class _DeclarationSet { |
| + _DeclarationSet() : _members = <mirrors.DeclarationMirror>[]; |
| + |
| + bool _checkType(obj, type) { |
| + if (obj == null) return true; |
| + return mirrors.reflectType(obj.runtimeType).isSubtypeOf(type); |
| + } |
| + |
| + bool _checkReturnType(value) { |
| + if (value == null) return true; |
| + var valueMirror = mirrors.reflectType(value.runtimeType); |
| + for (var member in _members) { |
| + if (member is mirrors.VariableMirror || member.isGetter) { |
| + // TODO(jacobr): actually check return types for getters that return |
| + // function types. |
| + return true; |
| + } else { |
| + if (valueMirror.isSubtypeOf(member.returnType)) return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + /** |
| + * Check whether the [invocation] is consistent with the [member] mirror. |
| + */ |
| + bool _checkDeclaration( |
| + Invocation invocation, mirrors.DeclarationMirror member) { |
| + if (member is mirrors.VariableMirror || member.isGetter) { |
| + // TODO(jacobr): actually check method types against the function type returned |
| + // by the getter or field. |
| + return true; |
| + } |
| + var parameters = member.parameters; |
| + var positionalArguments = invocation.positionalArguments; |
| + // Too many arguments |
| + if (parameters.length < positionalArguments.length) return false; |
| + // Too few required arguments. |
| + if (parameters.length > positionalArguments.length && |
| + !parameters[positionalArguments.length].isOptional) return false; |
| + var endPositional = math.min(parameters.length, positionalArguments.length); |
| + for (var i = 0; i < endPositional; i++) { |
| + if (parameters[i].isNamed) { |
| + // Not enough positional arguments. |
| + return false; |
| + } |
| + if (!_checkType( |
| + invocation.positionalArguments[i], parameters[i].type)) return false; |
| + } |
| + if (invocation.namedArguments.isNotEmpty) { |
| + var startPositional; |
| + for (startPositional = parameters.length - 1; |
| + startPositional >= 0; |
| + startPositional--) { |
| + if (!parameters[startPositional].isNamed) break; |
| + } |
| + startPositional++; |
| + |
| + for (var name in invocation.namedArguments.keys) { |
| + bool match = false; |
| + for (var j = startPositional; j < parameters.length; j++) { |
| + var p = parameters[j]; |
| + if (p.simpleName == name) { |
| + if (!_checkType(invocation.namedArguments[name], |
| + parameters[j].type)) return false; |
| + match = true; |
| + break; |
| + } |
| + } |
| + if (match == false) return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + bool checkInvocation(Invocation invocation) { |
| + for (var member in _members) { |
| + if (_checkDeclaration(invocation, member)) return true; |
| + } |
| + return false; |
| + } |
| + |
| + void add(mirrors.DeclarationMirror mirror) { |
| + _members.add(mirror); |
| + } |
| + |
| + final List<mirrors.DeclarationMirror> _members; |
| +} |
| + |
| +/** |
| + * Temporary method that we hope to remove at some point. This method should |
| + * generally only be called by machine generated code. |
| + */ |
| +void registerJsInterfaces(List<Type> classes) { |
| + if (_finalized == true) { |
| + throw 'JSInterop class registration already finalized'; |
| + } |
| + Map<Symbol, List<mirrors.DeclarationMirror>> allMembers; |
| + for (Type type in classes) { |
| + if (!_jsInterfaceTypes.add(type)) continue; // Already registered. |
| + mirrors.ClassMirror typeMirror = mirrors.reflectType(type); |
| + typeMirror.declarations.forEach((symbol, declaration) { |
| + if (declaration is mirrors.MethodMirror || |
| + declaration is mirrors.VariableMirror && !declaration.isStatic) { |
| + bool treatAsGetter = false; |
| + bool treatAsSetter = false; |
| + if (declaration is mirrors.VariableMirror) { |
| + treatAsGetter = true; |
| + if (!declaration.isConst && !declaration.isFinal) treatAsSetter = |
| + true; |
| + } else { |
| + if (declaration.isGetter) { |
| + _allowedGetters |
| + .putIfAbsent(symbol, () => new _DeclarationSet()) |
| + .add(declaration); |
| + _allowedMethods |
| + .putIfAbsent(symbol, () => new _DeclarationSet()) |
| + .add(declaration); |
| + treatAsGetter = true; |
| + } else if (declaration.isSetter) { |
| + treatAsSetter = true; |
| + } else if (!declaration.isConstructor) { |
| + _allowedMethods |
| + .putIfAbsent(symbol, () => new _DeclarationSet()) |
| + .add(declaration); |
| + } |
| + } |
| + if (treatAsGetter) { |
| + _allowedGetters |
| + .putIfAbsent(symbol, () => new _DeclarationSet()) |
| + .add(declaration); |
| + _allowedMethods |
| + .putIfAbsent(symbol, () => new _DeclarationSet()) |
| + .add(declaration); |
| + } |
| + if (treatAsSetter) { |
| + _allowedSetters |
| + .putIfAbsent(symbol, () => new _DeclarationSet()) |
| + .add(declaration); |
| + } |
| + } |
| + }); |
| + } |
| +} |
| + |
| +_finalizeJsInterfaces() native "Js_finalizeJsInterfaces"; |
| + |
| +/** |
| + * Generates a part file defining source code for JsObjectImpl and related |
| + * classes. This calass is needed so that type checks for all registered JavaScript |
| + * interop classes pass. |
| + */ |
| +String _generateJsObjectImplPart() { |
| + Iterable<Type> types = jsInterfaceTypes; |
| + var libraryPrefixes = new Map<mirrors.LibraryMirror, String>(); |
| + var prefixNames = new Set<String>(); |
| + var sb = new StringBuffer(); |
| + |
| + var implements = <String>[]; |
| + for (var type in types) { |
| + mirrors.ClassMirror typeMirror = mirrors.reflectType(type); |
| + mirrors.LibraryMirror libraryMirror = typeMirror.owner; |
| + var prefixName; |
| + if (libraryPrefixes.containsKey(libraryMirror)) { |
| + prefixName = libraryPrefixes[libraryMirror]; |
| + } else { |
| + var basePrefixName = |
| + mirrors.MirrorSystem.getName(libraryMirror.simpleName); |
| + basePrefixName = basePrefixName.replaceAll('.', '_'); |
| + if (basePrefixName.isEmpty) basePrefixName = "lib"; |
| + prefixName = basePrefixName; |
| + var i = 1; |
| + while (prefixNames.contains(prefixName)) { |
| + prefixName = '$basePrefixName$i'; |
| + i++; |
| + } |
| + prefixNames.add(prefixName); |
| + libraryPrefixes[libraryMirror] = prefixName; |
| + } |
| + implements.add( |
| + '${prefixName}.${mirrors.MirrorSystem.getName(typeMirror.simpleName)}'); |
| + } |
| + libraryPrefixes.forEach((libraryMirror, prefix) { |
| + sb.writeln('import "${libraryMirror.uri}" as $prefix;'); |
| + }); |
| + var implementsClause = |
| + implements.isEmpty ? "" : "implements ${implements.join(', ')}"; |
| + // TODO(jacobr): only certain classes need to be implemented by |
| + // Function and Array. |
| + sb.write(''' |
| +class JsObjectImpl extends JsObject $implementsClause { |
| + JsObjectImpl.internal() : super.internal(); |
| +} |
| + |
| +class JsFunctionImpl extends JsFunction $implementsClause { |
| + JsFunctionImpl.internal() : super.internal(); |
| +} |
| + |
| +class JsArrayImpl<E> extends JsArray<E> $implementsClause { |
| + JsArrayImpl.internal() : super.internal(); |
| +} |
| +'''); |
| + return sb.toString(); |
| +} |
| + |
| +// Start of block of helper methods facilitating emulating JavaScript Array |
| +// methods on Dart List objects passed to JavaScript via JS interop. |
| +// TODO(jacobr): match JS more closely. |
| +String _toStringJs(obj) => '$obj'; |
| + |
| +// TODO(jacobr): this might not exactly match JS semantics but should be |
| +// adequate for now. |
| +int _toIntJs(obj) { |
| + if (obj is int) return obj; |
| + if (obj is num) return obj.toInt(); |
| + return num.parse('$obj'.trim(), (_) => 0).toInt(); |
| +} |
| + |
| +// TODO(jacobr): this might not exactly match JS semantics but should be |
| +// adequate for now. |
| +num _toNumJs(obj) { |
| + return obj is num ? obj : num.parse('$obj'.trim(), (_) => 0); |
| +} |
| + |
| +/// Match the behavior of setting List length in JavaScript with the exception |
| +/// that Dart does not distinguish undefined and null. |
| +_setListLength(List list, rawlen) { |
| + num len = _toNumJs(rawlen); |
| + if (len is! int || len < 0) { |
| + throw new RangeError("Invalid array length"); |
| + } |
| + if (len > list.length) { |
| + _arrayExtend(list, len); |
| + } else if (len < list.length) { |
| + list.removeRange(len, list.length); |
| + } |
| + return rawlen; |
| +} |
| + |
| +// TODO(jacobr): should we really bother with this method instead of just |
| +// shallow copying to a JS array and calling the JavaScript join method? |
| +String _arrayJoin(List list, sep) { |
| + if (sep == null) { |
| + sep = ","; |
| + } |
| + return list.map((e) => e == null ? "" : e.toString()).join(sep.toString()); |
| +} |
| + |
| +// TODO(jacobr): should we really bother with this method instead of just |
| +// shallow copying to a JS array and using the toString method? |
| +String _arrayToString(List list) => _arrayJoin(list, ","); |
| + |
| +int _arrayPush(List list, e) { |
| + list.add(e); |
| + return list.length; |
| +} |
| + |
| +_arrayPop(List list) => list.removeLast(); |
| + |
| +// TODO(jacobr): would it be better to just copy input to a JS List |
| +// and call Array.concat? |
| +List _arrayConcat(List input, List args) { |
| + var ret = new List.from(input); |
| + for (var e in args) { |
| + // TODO(jacobr): technically in ES6 we should use |
| + // Symbol.isConcatSpreadable to determine whether call addAll. Once v8 |
| + // supports it, we can make all Dart classes implementing Iterable |
| + // specify isConcatSpreadable and tweak this behavior to allow Iterable. |
| + if (e is List) { |
| + ret.addAll(e); |
| + } else { |
| + ret.add(e); |
| + } |
| + } |
| + return ret; |
| +} |
| + |
| +List _arraySplice(List input, List args) { |
| + int start = 0; |
| + if (args.length > 0) { |
| + var rawStart = _toIntJs(args[0]); |
| + if (rawStart < 0) { |
| + start = math.max(0, input.length - rawStart); |
| + } else { |
| + start = math.min(input.length, rawStart); |
| + } |
| + } |
| + var end = start; |
| + if (args.length > 1) { |
| + var rawDeleteCount = _toIntJs(args[1]); |
| + if (rawDeleteCount < 0) rawDeleteCount = 0; |
| + end = math.min(input.length, start + rawDeleteCount); |
| + } |
| + var replacement = []; |
| + var removedElements = input.getRange(start, end).toList(); |
| + if (args.length > 2) { |
| + replacement = args.getRange(2, args.length); |
| + } |
| + input.replaceRange(start, end, replacement); |
| + return removedElements; |
| +} |
| + |
| +List _arrayReverse(List l) { |
| + for (var i = 0, j = l.length - 1; i < j; i++, j--) { |
| + var tmp = l[i]; |
| + l[i] = l[j]; |
| + l[j] = tmp; |
| + } |
| + return l; |
| +} |
| + |
| +_arrayShift(List l) { |
| + if (l.isEmpty) return null; // Technically we should return undefined. |
| + return l.removeAt(0); |
| +} |
| + |
| +int _arrayUnshift(List l, List args) { |
| + l.insertAll(0, args); |
| + return l.length; |
| +} |
| + |
| +_arrayExtend(List l, int newLength) { |
| + for (var i = l.length; i < newLength; i++) { |
| + // TODO(jacobr): we'd really like to add undefined to better match |
| + // JavaScript semantics. |
| + l.add(null); |
| + } |
| +} |
| + |
| +List _arraySort(List l, rawCompare) { |
| + // TODO(jacobr): alternately we could just copy the Array to JavaScript, |
| + // invoke the JS sort method and then copy the result back to Dart. |
| + Comparator compare; |
| + if (rawCompare == null) { |
| + compare = (a, b) => _toStringJs(a).compareTo(_toStringJs(b)); |
| + } else if (rawCompare is JsFunction) { |
| + compare = (a, b) => rawCompare.apply([a, b]); |
| + } else { |
| + compare = rawCompare; |
| + } |
| + l.sort(compare); |
| + return l; |
| +} |
| +// End of block of helper methods to emulate JavaScript Array methods on Dart List. |
| + |
| +/** |
| + * Can be called to provide a predictable point where no more JS interfaces can |
| + * be added. Creating an instance of JsObject will also automatically trigger |
| + * all JsObjects to be finalized. |
| + */ |
| +void finalizeJsInterfaces() { |
| + if (_finalized == true) { |
| + throw 'JSInterop class registration already finalized'; |
| + } |
| + _finalizeJsInterfaces(); |
| +} |
| JsObject _cachedContext; |
| JsObject get _context native "Js_context_Callback"; |
| +bool get _finalized native "Js_interfacesFinalized_Callback"; |
| + |
| JsObject get context { |
| if (_cachedContext == null) { |
| _cachedContext = _context; |
| @@ -114,9 +486,24 @@ class JsObject extends NativeFieldWrapperClass2 { |
| * Constructs a new JavaScript object from [constructor] and returns a proxy |
| * to it. |
| */ |
| - factory JsObject(JsFunction constructor, [List arguments]) => _create(constructor, arguments); |
| - |
| - static JsObject _create(JsFunction constructor, arguments) native "JsObject_constructorCallback"; |
| + factory JsObject(JsFunction constructor, [List arguments]) => |
| + _create(constructor, arguments); |
| + |
| + static JsObject _create( |
| + JsFunction constructor, arguments) native "JsObject_constructorCallback"; |
| + |
| + _buildArgs(Invocation invocation) { |
| + if (invocation.namedArguments.isEmpty) { |
| + return invocation.positionalArguments; |
| + } else { |
| + var varArgs = new Map<String, Object>(); |
| + invocation.namedArguments.forEach((symbol, val) { |
| + varArgs[mirrors.MirrorSystem.getName(symbol)] = val; |
| + }); |
| + return invocation.positionalArguments.toList() |
| + ..add(new JsObject.jsify(varArgs)); |
| + } |
| + } |
| /** |
| * Constructs a [JsObject] that proxies a native Dart object; _for expert use |
| @@ -131,8 +518,7 @@ class JsObject extends NativeFieldWrapperClass2 { |
| */ |
| factory JsObject.fromBrowserObject(object) { |
| if (object is num || object is String || object is bool || object == null) { |
| - throw new ArgumentError( |
| - "object cannot be a num, string, bool, or null"); |
| + throw new ArgumentError("object cannot be a num, string, bool, or null"); |
| } |
| return _fromBrowserObject(object); |
| } |
| @@ -155,7 +541,8 @@ class JsObject extends NativeFieldWrapperClass2 { |
| static JsObject _jsify(object) native "JsObject_jsify"; |
| - static JsObject _fromBrowserObject(object) native "JsObject_fromBrowserObject"; |
| + static JsObject _fromBrowserObject( |
| + object) native "JsObject_fromBrowserObject"; |
|
terry
2015/06/19 14:57:04
Not your problem but ugly pretty printing. Wonder
terry
2015/06/19 16:42:11
Talked to Bob added issue to track this see https:
Jacob
2015/06/19 20:39:43
Acknowledged.
Jacob
2015/06/19 20:39:43
Acknowledged.
|
| /** |
| * Returns the value associated with [property] from the proxied JavaScript |
| @@ -163,7 +550,7 @@ class JsObject extends NativeFieldWrapperClass2 { |
| * |
| * The type of [property] must be either [String] or [num]. |
| */ |
| - operator[](property) native "JsObject_[]"; |
| + operator [](property) native "JsObject_[]"; |
| /** |
| * Sets the value associated with [property] on the proxied JavaScript |
| @@ -171,13 +558,14 @@ class JsObject extends NativeFieldWrapperClass2 { |
| * |
| * The type of [property] must be either [String] or [num]. |
| */ |
| - operator[]=(property, value) native "JsObject_[]="; |
| + operator []=(property, value) native "JsObject_[]="; |
| int get hashCode native "JsObject_hashCode"; |
| - operator==(other) => other is JsObject && _identityEquality(this, other); |
| + operator ==(other) => other is JsObject && _identityEquality(this, other); |
| - static bool _identityEquality(JsObject a, JsObject b) native "JsObject_identityEquality"; |
| + static bool _identityEquality( |
| + JsObject a, JsObject b) native "JsObject_identityEquality"; |
| /** |
| * Returns `true` if the JavaScript object contains the specified property |
| @@ -207,7 +595,7 @@ class JsObject extends NativeFieldWrapperClass2 { |
| String toString() { |
| try { |
| return _toString(); |
| - } catch(e) { |
| + } catch (e) { |
| return super.toString(); |
| } |
| } |
| @@ -223,7 +611,7 @@ class JsObject extends NativeFieldWrapperClass2 { |
| callMethod(String method, [List args]) { |
| try { |
| return _callMethod(method, args); |
| - } catch(e) { |
| + } catch (e) { |
| if (hasProperty(method)) { |
| rethrow; |
| } else { |
| @@ -232,13 +620,63 @@ class JsObject extends NativeFieldWrapperClass2 { |
| } |
| } |
| + noSuchMethod(Invocation invocation) { |
| + throwError() { |
| + throw new NoSuchMethodError(this, invocation.memberName, |
| + invocation.positionalArguments, invocation.namedArguments); |
| + } |
| + |
| + String name = mirrors.MirrorSystem.getName(invocation.memberName); |
| + if (invocation.isGetter) { |
| + if (_CHECK_JS_INVOCATIONS) { |
| + var matches = _allowedGetters[invocation.memberName]; |
| + if (matches == null && |
| + !_allowedMethods.containsKey(invocation.memberName)) { |
| + throwError(); |
| + } |
| + var ret = this[name]; |
| + if (matches != null && matches._checkReturnType(ret)) return ret; |
| + if (ret is Function || |
| + (ret is JsFunction /* shouldn't be needed in the future*/) && |
| + _allowedMethods.containsKey( |
| + invocation.memberName)) return ret; // Warning: we have not bound "this"... we could type check on the Function but that is of little value in Dart. |
| + throwError(); |
| + } else { |
| + // TODO(jacobr): should we throw if the JavaScript object doesn't have the property? |
| + return this[name]; |
| + } |
| + } else if (invocation.isSetter) { |
| + if (_CHECK_JS_INVOCATIONS) { |
| + var matches = _allowedSetters[invocation.memberName]; |
| + if (matches == null || |
| + !matches.checkInvocation(invocation)) throwError(); |
| + } |
| + assert(name.endsWith("=")); |
| + name = name.substring(0, name.length - 1); |
| + return this[name] = invocation.positionalArguments.first; |
| + } else { |
| + // TODO(jacobr): also allow calling getters that look like functions. |
| + var matches; |
| + if (_CHECK_JS_INVOCATIONS) { |
| + matches = _allowedMethods[invocation.memberName]; |
| + if (matches == null || |
| + !matches.checkInvocation(invocation)) throwError(); |
| + } |
| + var ret = this.callMethod(name, _buildArgs(invocation)); |
| + if (_CHECK_JS_INVOCATIONS) { |
| + if (!matches._checkReturnType(ret)) throwError(); |
| + } |
| + return ret; |
| + } |
| + } |
| + |
| _callMethod(String name, List args) native "JsObject_callMethod"; |
| } |
| /** |
| * Proxies a JavaScript Function object. |
| */ |
| -class JsFunction extends JsObject { |
| +class JsFunction extends JsObject implements Function { |
| JsFunction.internal() : super.internal(); |
| /** |
| @@ -253,13 +691,21 @@ class JsFunction extends JsObject { |
| */ |
| dynamic apply(List args, {thisArg}) native "JsFunction_apply"; |
| + noSuchMethod(Invocation invocation) { |
| + if (invocation.isMethod && invocation.memberName == #call) { |
| + return apply(_buildArgs(invocation)); |
| + } |
| + return super.noSuchMethod(invocation); |
| + } |
| + |
| /** |
| * Internal only version of apply which uses debugger proxies of Dart objects |
| * rather than opaque handles. This method is private because it cannot be |
| * efficiently implemented in Dart2Js so should only be used by internal |
| * tools. |
| */ |
| - _applyDebuggerOnly(List args, {thisArg}) native "JsFunction_applyDebuggerOnly"; |
| + _applyDebuggerOnly(List args, |
| + {thisArg}) native "JsFunction_applyDebuggerOnly"; |
| static JsFunction _withThis(Function f) native "JsFunction_withThis"; |
| } |
| @@ -268,14 +714,17 @@ class JsFunction extends JsObject { |
| * A [List] proxying a JavaScript Array. |
| */ |
| class JsArray<E> extends JsObject with ListMixin<E> { |
| + JsArray.internal() : super.internal(); |
| factory JsArray() => _newJsArray(); |
| static JsArray _newJsArray() native "JsArray_newJsArray"; |
| - factory JsArray.from(Iterable<E> other) => _newJsArrayFromSafeList(new List.from(other)); |
| + factory JsArray.from(Iterable<E> other) => |
| + _newJsArrayFromSafeList(new List.from(other)); |
| - static JsArray _newJsArrayFromSafeList(List list) native "JsArray_newJsArrayFromSafeList"; |
| + static JsArray _newJsArrayFromSafeList( |
| + List list) native "JsArray_newJsArrayFromSafeList"; |
| _checkIndex(int index, {bool insert: false}) { |
| int length = insert ? this.length + 1 : this.length; |
| @@ -304,7 +753,7 @@ class JsArray<E> extends JsObject with ListMixin<E> { |
| } |
| void operator []=(index, E value) { |
| - if(index is int) { |
| + if (index is int) { |
| _checkIndex(index); |
| } |
| super[index] = value; |
| @@ -312,7 +761,9 @@ class JsArray<E> extends JsObject with ListMixin<E> { |
| int get length native "JsArray_length"; |
| - void set length(int length) { super['length'] = length; } |
| + void set length(int length) { |
| + super['length'] = length; |
| + } |
| // Methods overriden for better performance |
| @@ -326,7 +777,7 @@ class JsArray<E> extends JsObject with ListMixin<E> { |
| } |
| void insert(int index, E element) { |
| - _checkIndex(index, insert:true); |
| + _checkIndex(index, insert: true); |
| callMethod('splice', [index, 0, element]); |
| } |
| @@ -365,7 +816,7 @@ class JsArray<E> extends JsObject with ListMixin<E> { |
| */ |
| const _UNDEFINED = const Object(); |
| -// FIXME(jacobr): this method is a hack to work around the lack of proper dart |
| +// TODO(jacobr): this method is a hack to work around the lack of proper dart |
| // support for varargs methods. |
| List _stripUndefinedArgs(List args) => |
| args.takeWhile((i) => i != _UNDEFINED).toList(); |
| @@ -375,8 +826,7 @@ List _stripUndefinedArgs(List args) => |
| * than 11) of arguments without violating Dart type checks. |
| */ |
| Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => |
| - ([a1=_UNDEFINED, a2=_UNDEFINED, a3=_UNDEFINED, a4=_UNDEFINED, |
| - a5=_UNDEFINED, a6=_UNDEFINED, a7=_UNDEFINED, a8=_UNDEFINED, |
| - a9=_UNDEFINED, a10=_UNDEFINED]) => |
| - jsFunction._applyDebuggerOnly(_stripUndefinedArgs( |
| - [a1,a2,a3,a4,a5,a6,a7,a8,a9,a10])); |
| + ([a1 = _UNDEFINED, a2 = _UNDEFINED, a3 = _UNDEFINED, a4 = _UNDEFINED, |
| + a5 = _UNDEFINED, a6 = _UNDEFINED, a7 = _UNDEFINED, a8 = _UNDEFINED, |
| + a9 = _UNDEFINED, a10 = _UNDEFINED]) => jsFunction._applyDebuggerOnly( |
| + _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); |