| // 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. |
| |
| import '../common.dart'; |
| import '../constants/values.dart'; |
| import '../elements/resolution_types.dart'; |
| import '../elements/elements.dart'; |
| import '../js/js.dart' as js; |
| import '../js_emitter/js_emitter.dart' show NativeEmitter; |
| import '../ssa/builder.dart' show SsaAstGraphBuilder; |
| import '../ssa/nodes.dart' show HInstruction, HForeignCode, HReturn; |
| import '../tree/tree.dart'; |
| import '../universe/side_effects.dart' show SideEffects; |
| |
| final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); |
| |
| void handleSsaNative(SsaAstGraphBuilder builder, Expression nativeBody) { |
| MethodElement element = builder.target; |
| NativeEmitter nativeEmitter = builder.nativeEmitter; |
| |
| HInstruction convertDartClosure( |
| ParameterElement parameter, ResolutionFunctionType type) { |
| HInstruction local = builder.localsHandler.readLocal(parameter); |
| ConstantValue arityConstant = |
| builder.constantSystem.createInt(type.parameterTypes.length); |
| HInstruction arity = |
| builder.graph.addConstant(arityConstant, builder.closedWorld); |
| // TODO(ngeoffray): For static methods, we could pass a method with a |
| // defined arity. |
| MethodElement helper = builder.commonElements.closureConverter; |
| 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(builder.nativeData.isNativeMember(element)); |
| String nativeMethodName = builder.nativeData.getFixedBackendName(element); |
| if (nativeBody != null) { |
| LiteralString jsCode = nativeBody.asLiteralString(); |
| String str = jsCode.dartString.slowToString(); |
| if (nativeRedirectionRegExp.hasMatch(str)) { |
| failedAt(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((_parameter) { |
| ParameterElement parameter = _parameter; |
| ResolutionDartType type = parameter.type.unaliased; |
| HInstruction input = builder.localsHandler.readLocal(parameter); |
| if (type is ResolutionFunctionType) { |
| // 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 { |
| failedAt(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), |
| builder.commonMasks.dynamicType, |
| inputs, |
| effects: new SideEffects())); |
| // TODO(johnniwinther): Provide source information. |
| builder |
| .close(new HReturn(builder.pop(), null)) |
| .addSuccessor(builder.graph.exit); |
| } else { |
| if (parameters.parameterCount != 0) { |
| failedAt( |
| 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, |
| builder.commonMasks.dynamicType)); |
| } |
| } |