blob: 09f4e03d65d39364d49e03ae00a423e71983fe64 [file] [log] [blame]
// Copyright (c) 2014, 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.
part of native;
final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$');
void handleSsaNative(SsaBuilder builder, Expression nativeBody) {
Compiler compiler = builder.compiler;
FunctionElement element = builder.work.element;
NativeEmitter nativeEmitter = builder.nativeEmitter;
JavaScriptBackend backend = builder.backend;
HInstruction convertDartClosure(ParameterElement parameter,
FunctionType type) {
HInstruction local = builder.localsHandler.readLocal(parameter);
ConstantValue arityConstant =
builder.constantSystem.createInt(type.computeArity());
HInstruction arity = builder.graph.addConstant(arityConstant, compiler);
// TODO(ngeoffray): For static methods, we could pass a method with a
// defined arity.
Element helper = backend.getClosureConverter();
builder.pushInvokeStatic(nativeBody, helper, [local, arity]);
HInstruction closure = builder.pop();
return closure;
}
// Check which pattern this native method follows:
// 1) foo() native;
// hasBody = false
// 2) foo() native "bar";
// No longer supported, this is now done with @JSName('foo') and case 1.
// 3) foo() native "return 42";
// hasBody = true
bool hasBody = false;
assert(element.isNative);
String nativeMethodName = element.fixedBackendName;
if (nativeBody != null) {
LiteralString jsCode = nativeBody.asLiteralString();
String str = jsCode.dartString.slowToString();
if (nativeRedirectionRegExp.hasMatch(str)) {
compiler.internalError(
nativeBody, "Deprecated syntax, use @JSName('name') instead.");
}
hasBody = true;
}
if (!hasBody) {
nativeEmitter.nativeMethods.add(element);
}
FunctionSignature parameters = element.functionSignature;
if (!hasBody) {
List<String> arguments = <String>[];
List<HInstruction> inputs = <HInstruction>[];
String receiver = '';
if (element.isInstanceMember) {
receiver = '#.';
inputs.add(builder.localsHandler.readThis());
}
parameters.forEachParameter((ParameterElement parameter) {
DartType type = parameter.type.unalias(compiler);
HInstruction input = builder.localsHandler.readLocal(parameter);
if (type is FunctionType) {
// The parameter type is a function type either directly or through
// typedef(s).
input = convertDartClosure(parameter, type);
}
inputs.add(input);
arguments.add('#');
});
String foreignParameters = arguments.join(',');
String nativeMethodCall;
if (element.kind == ElementKind.FUNCTION) {
nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)';
} else if (element.kind == ElementKind.GETTER) {
nativeMethodCall = '$receiver$nativeMethodName';
} else if (element.kind == ElementKind.SETTER) {
nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters';
} else {
builder.compiler.internalError(element,
'Unexpected kind: "${element.kind}".');
}
builder.push(
new HForeignCode(
// TODO(sra): This could be cached. The number of templates should
// be proportional to the number of native methods, which is bounded
// by the dart: libraries.
js.js.uncachedExpressionTemplate(nativeMethodCall),
backend.dynamicType,
inputs, effects: new SideEffects()));
builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit);
} else {
if (parameters.parameterCount != 0) {
compiler.internalError(nativeBody,
'native "..." syntax is restricted to '
'functions with zero parameters.');
}
LiteralString jsCode = nativeBody.asLiteralString();
builder.push(new HForeignCode.statement(
js.js.statementTemplateYielding(
new js.LiteralStatement(jsCode.dartString.slowToString())),
<HInstruction>[],
new SideEffects(),
null,
backend.dynamicType));
}
}