mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 22:11:19 +00:00
Implement Function.apply in dart2js.
Review URL: https://codereview.chromium.org//11093015 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@13409 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
b38daebd36
commit
e24c0e7af5
|
@ -131,6 +131,7 @@ abstract class Compiler implements DiagnosticListener {
|
|||
ClassElement listClass;
|
||||
Element assertMethod;
|
||||
Element identicalFunction;
|
||||
Element functionApplyMethod;
|
||||
|
||||
Element get currentElement => _currentElement;
|
||||
withCurrentElement(Element element, f()) {
|
||||
|
@ -178,6 +179,7 @@ abstract class Compiler implements DiagnosticListener {
|
|||
const SourceString('startRootIsolate');
|
||||
bool enabledNoSuchMethod = false;
|
||||
bool enabledRuntimeType = false;
|
||||
bool enabledFunctionApply = false;
|
||||
|
||||
Stopwatch progress;
|
||||
|
||||
|
@ -384,6 +386,10 @@ abstract class Compiler implements DiagnosticListener {
|
|||
identicalFunction = coreLibrary.find(const SourceString('identical'));
|
||||
|
||||
initializeSpecialClasses();
|
||||
|
||||
functionClass.ensureResolved(this);
|
||||
functionApplyMethod =
|
||||
functionClass.lookupLocalMember(const SourceString('apply'));
|
||||
}
|
||||
|
||||
void loadCoreImplLibrary() {
|
||||
|
|
|
@ -101,6 +101,8 @@ class Enqueuer {
|
|||
// runtime type.
|
||||
if (element.isGetter() && element.name == Compiler.RUNTIME_TYPE) {
|
||||
compiler.enabledRuntimeType = true;
|
||||
} else if (element == compiler.functionApplyMethod) {
|
||||
compiler.enabledFunctionApply = true;
|
||||
}
|
||||
|
||||
// Enable isolate support if we start using something from the
|
||||
|
|
|
@ -476,20 +476,97 @@ function(prototype, staticName, fieldName, getterName, lazyValue) {
|
|||
//
|
||||
// We need to generate a stub for (5) because the order of the
|
||||
// stub arguments and the real method may be different.
|
||||
Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
|
||||
if (selectors == null) return;
|
||||
|
||||
// Keep a cache of which stubs have already been generated, to
|
||||
// avoid duplicates. Note that even if selectors are
|
||||
// canonicalized, we would still need this cache: a typed selector
|
||||
// on A and a typed selector on B could yield the same stub.
|
||||
Set<String> generatedStubNames = new Set<String>();
|
||||
for (Selector selector in selectors) {
|
||||
if (!selector.applies(member, compiler)) continue;
|
||||
addParameterStub(
|
||||
member, selector, defineInstanceMember, generatedStubNames);
|
||||
if (compiler.enabledFunctionApply
|
||||
&& member.name == Namer.CLOSURE_INVOCATION_NAME) {
|
||||
// If [Function.apply] is called, we pessimistically compile all
|
||||
// possible stubs for this closure.
|
||||
// TODO(5074): This functionality only supports the new
|
||||
// parameter specification, and this comment should be removed
|
||||
// once the old specification is not supported.
|
||||
FunctionSignature signature = member.computeSignature(compiler);
|
||||
Set<Selector> selectors = signature.optionalParametersAreNamed
|
||||
? computeNamedSelectors(signature, member)
|
||||
: computeOptionalSelectors(signature, member);
|
||||
for (Selector selector in selectors) {
|
||||
addParameterStub(
|
||||
member, selector, defineInstanceMember, generatedStubNames);
|
||||
}
|
||||
} else {
|
||||
Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
|
||||
if (selectors == null) return;
|
||||
for (Selector selector in selectors) {
|
||||
if (!selector.applies(member, compiler)) continue;
|
||||
addParameterStub(
|
||||
member, selector, defineInstanceMember, generatedStubNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the set of possible selectors in the presence of named
|
||||
* parameters.
|
||||
*/
|
||||
Set<Selector> computeNamedSelectors(FunctionSignature signature,
|
||||
FunctionElement element) {
|
||||
Set<Selector> selectors = new Set<Selector>();
|
||||
// Add the selector that does not have any optional argument.
|
||||
selectors.add(new Selector(SelectorKind.CALL,
|
||||
element.name,
|
||||
element.getLibrary(),
|
||||
signature.requiredParameterCount,
|
||||
<SourceString>[]));
|
||||
|
||||
// For each optional parameter, we iterator over the set of
|
||||
// already computed selectors and create new selectors with that
|
||||
// parameter now being passed.
|
||||
signature.forEachOptionalParameter((Element element) {
|
||||
Set<Selector> newSet = new Set<Selector>();
|
||||
selectors.forEach((Selector other) {
|
||||
List<SourceString> namedArguments = [element.name];
|
||||
namedArguments.addAll(other.namedArguments);
|
||||
newSet.add(new Selector(other.kind,
|
||||
other.name,
|
||||
other.library,
|
||||
other.argumentCount + 1,
|
||||
namedArguments));
|
||||
});
|
||||
selectors.addAll(newSet);
|
||||
});
|
||||
return selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the set of possible selectors in the presence of optional
|
||||
* non-named parameters.
|
||||
*/
|
||||
Set<Selector> computeOptionalSelectors(FunctionSignature signature,
|
||||
FunctionElement element) {
|
||||
Set<Selector> selectors = new Set<Selector>();
|
||||
// Add the selector that does not have any optional argument.
|
||||
selectors.add(new Selector(SelectorKind.CALL,
|
||||
element.name,
|
||||
element.getLibrary(),
|
||||
signature.requiredParameterCount,
|
||||
<SourceString>[]));
|
||||
|
||||
// For each optional parameter, we increment the number of passed
|
||||
// argument.
|
||||
for (int i = 1; i <= signature.optionalParameterCount; i++) {
|
||||
selectors.add(new Selector(SelectorKind.CALL,
|
||||
element.name,
|
||||
element.getLibrary(),
|
||||
signature.requiredParameterCount + i,
|
||||
<SourceString>[]));
|
||||
}
|
||||
return selectors;
|
||||
}
|
||||
|
||||
bool instanceFieldNeedsGetter(Element member) {
|
||||
assert(member.isField());
|
||||
return compiler.codegenWorld.hasInvokedGetter(member, compiler);
|
||||
|
|
|
@ -34,7 +34,8 @@ patch class Function {
|
|||
patch static apply(Function function,
|
||||
List positionalArguments,
|
||||
[Map<String,Dynamic> namedArguments]) {
|
||||
throw new NotImplementedException("Function.apply is not implemented");
|
||||
return Primitives.applyFunction(
|
||||
function, positionalArguments, namedArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -610,6 +610,46 @@ class Primitives {
|
|||
}
|
||||
JS('void', '#[#] = #', object, key, value);
|
||||
}
|
||||
|
||||
static applyFunction(Function function,
|
||||
List positionalArguments,
|
||||
Map<String, Dynamic> namedArguments) {
|
||||
int argumentCount = 0;
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
List arguments = [];
|
||||
|
||||
if (positionalArguments != null) {
|
||||
argumentCount += positionalArguments.length;
|
||||
arguments.addAll(positionalArguments);
|
||||
}
|
||||
|
||||
// Sort the named arguments to get the right selector name and
|
||||
// arguments order.
|
||||
if (namedArguments != null && !namedArguments.isEmpty()) {
|
||||
// Call new List.from to make sure we get a JavaScript array.
|
||||
List<String> listOfNamedArguments =
|
||||
new List<String>.from(namedArguments.getKeys());
|
||||
argumentCount += namedArguments.length;
|
||||
// We're sorting on strings, and the behavior is the same between
|
||||
// Dart string sort and JS string sort. To avoid needing the Dart
|
||||
// sort implementation, we use the JavaScript one instead.
|
||||
JS('void', '#.sort()', listOfNamedArguments);
|
||||
listOfNamedArguments.forEach((String name) {
|
||||
buffer.add('\$$name');
|
||||
arguments.add(namedArguments[name]);
|
||||
});
|
||||
}
|
||||
|
||||
String selectorName = 'call\$$argumentCount$buffer';
|
||||
var jsFunction = JS('var', '#[#]', function, selectorName);
|
||||
if (jsFunction == null) {
|
||||
throw new NoSuchMethodError(function, selectorName, arguments);
|
||||
}
|
||||
// We bound 'this' to [function] because of how we compile
|
||||
// closures: escaped local variables are stored and accessed through
|
||||
// [function].
|
||||
return JS('var', '#.apply(#, #)', jsFunction, function, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,46 +1,10 @@
|
|||
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
//
|
||||
// This test should move to a language test once the apply method
|
||||
// gets into the specification.
|
||||
|
||||
apply(Function function, ArgumentDescriptor args) {
|
||||
int argumentCount = 0;
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
List arguments = [];
|
||||
|
||||
if (args.positionalArguments != null) {
|
||||
argumentCount += args.positionalArguments.length;
|
||||
arguments.addAll(args.positionalArguments);
|
||||
}
|
||||
|
||||
// Sort the named arguments to get the right selector name and
|
||||
// arguments order.
|
||||
if (args.namedArguments != null && !args.namedArguments.isEmpty()) {
|
||||
// Call new List.from to make sure we get a JavaScript array.
|
||||
List<String> namedArguments =
|
||||
new List<String>.from(args.namedArguments.getKeys());
|
||||
argumentCount += namedArguments.length;
|
||||
// We're sorting on strings, and the behavior is the same between
|
||||
// Dart string sort and JS String sort. To avoid needing the Dart
|
||||
// sort implementation, we use the JavaScript one instead.
|
||||
JS('void', '#.sort()', namedArguments);
|
||||
namedArguments.forEach((String name) {
|
||||
buffer.add('\$$name');
|
||||
arguments.add(args.namedArguments[name]);
|
||||
});
|
||||
}
|
||||
|
||||
String selectorName = 'call\$$argumentCount$buffer';
|
||||
var jsFunction = JS('var', '#[#]', function, selectorName);
|
||||
if (jsFunction == null) {
|
||||
throw new NoSuchMethodError(function, selectorName, arguments);
|
||||
}
|
||||
// We bound 'this' to [function] because of how we compile
|
||||
// closures: escaped local variables are stored and accessed through
|
||||
// [function].
|
||||
return JS('var', '#.apply(#, #)', jsFunction, function, arguments);
|
||||
return Function.apply(
|
||||
function, args.positionalArguments, args.namedArguments);
|
||||
}
|
||||
|
||||
class ArgumentDescriptor {
|
||||
|
@ -62,17 +26,6 @@ main() {
|
|||
var c5 = ({a: 1, b: 2}) => 'c5 $a $b';
|
||||
var c6 = ({b: 1, a: 2}) => 'c6 $a $b';
|
||||
|
||||
// TODO(ngeoffray): Remove these calls. They are currently needed
|
||||
// because otherwise we would not generate the stubs. Once apply is
|
||||
// in the specification, we should change the compiler to emit stubs
|
||||
// for closures once it sees an 'apply' selector.
|
||||
c1();
|
||||
c2(1);
|
||||
c3(); c3(1);
|
||||
c4(); c4(a: 1);
|
||||
c5(); c5(a: 1); c5(b: 2); c5(a:1, b: 2);
|
||||
c6(); c6(a: 1); c6(b: 2); c6(a:1, b: 2);
|
||||
|
||||
Expect.equals('c1', apply(c1, new ArgumentDescriptor(null, null)));
|
||||
Expect.equals('c1', apply(c1, new ArgumentDescriptor([], null)));
|
||||
Expect.equals('c1', apply(c1, new ArgumentDescriptor([], {})));
|
||||
|
@ -91,9 +44,7 @@ main() {
|
|||
Expect.equals('c3 1', apply(c3, new ArgumentDescriptor([], null)));
|
||||
Expect.equals('c3 2', apply(c3, new ArgumentDescriptor([2], {})));
|
||||
throwsNSME(() => apply(c3, new ArgumentDescriptor([1, 2], null)));
|
||||
// TODO(ngeoffray): Should be throwsNSME with the new parameter
|
||||
// specification.
|
||||
Expect.equals('c3 1', apply(c3, new ArgumentDescriptor(null, {'a': 1})));
|
||||
throwsNSME(() => apply(c3, new ArgumentDescriptor(null, {'a': 1})));
|
||||
|
||||
Expect.equals('c4 1', apply(c4, new ArgumentDescriptor([], null)));
|
||||
Expect.equals('c4 2', apply(c4, new ArgumentDescriptor([], {'a': 2})));
|
|
@ -48,7 +48,7 @@ main() {
|
|||
Expect.equals(res, Function.apply(func, list, map));
|
||||
}
|
||||
testList(42, test0, null);
|
||||
testList(42, test0, []]);
|
||||
testList(42, test0, []);
|
||||
testMap(42, test0a, {"a": 5});
|
||||
testList(42, test1, [41]);
|
||||
test(42, test1a, [20], {"a": 22});
|
||||
|
|
|
@ -9,6 +9,7 @@ compare_to2_test: Fail # Bug 4018
|
|||
null_test: Fail # Bug 5511
|
||||
null_nosuchmethod_test: Fail # Bug 5518
|
||||
apply_test: Fail # Bug 5670
|
||||
apply2_test: Fail # Bug 5670
|
||||
|
||||
[ $compiler == dartc ]
|
||||
apply_test: Fail # Bug 5670
|
||||
|
@ -38,7 +39,6 @@ unicode_test: Fail
|
|||
[ $compiler == dart2js ]
|
||||
math_parse_double_test: Fail # Expect.equals(expected: <78187493520>, actual: <0>)
|
||||
math_test: Fail # issue 3333
|
||||
apply_test: Fail # issue 5669
|
||||
|
||||
# Bad test, assumes RegExp.allMatches returns a Collection.
|
||||
reg_exp_all_matches_test: Fail, OK # NoSuchMethodError : method not found: 'forEach'
|
||||
|
@ -66,7 +66,8 @@ reg_exp_all_matches_test: Fail # BUG(3304): Maybe this doesn't time out?
|
|||
string_base_vm_test: Fail # BUG(3304): Maybe this doesn't time out?
|
||||
|
||||
[ $compiler == dart2dart ]
|
||||
apply_test: Fail # inherited from dart2js
|
||||
apply_test: Fail # inherited from VM
|
||||
apply2_test: Fail # inherited from VM
|
||||
compare_to2_test: Fail # inherited from VM
|
||||
null_nosuchmethod_test: Fail # inherited from VM
|
||||
null_test: Fail # inherited from VM
|
||||
|
|
Loading…
Reference in a new issue