Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 45 | 45 |
| 46 #include <dart_api.h> | 46 #include <dart_api.h> |
| 47 #include <limits> | 47 #include <limits> |
| 48 | 48 |
| 49 namespace blink { | 49 namespace blink { |
| 50 | 50 |
| 51 const int JsObject::dartClassId = _JsObjectClassId; | 51 const int JsObject::dartClassId = _JsObjectClassId; |
| 52 const int JsFunction::dartClassId = _JsFunctionClassId; | 52 const int JsFunction::dartClassId = _JsFunctionClassId; |
| 53 const int JsArray::dartClassId = _JsArrayClassId; | 53 const int JsArray::dartClassId = _JsArrayClassId; |
| 54 | 54 |
| 55 // TODO(jacobr): v8::String::NewFromUtf8 usages should be cached for constant | |
| 56 // strings to improve performance. | |
| 57 /** | |
| 58 * Polyfill script to make Dart List objects look like JavaScript arrays when | |
| 59 * passed to JavaScript via JavaScript interop. Handling the edge cases | |
| 60 * requires patching the JavaScript Array prototype for a couple methods | |
| 61 * and implementing all of the JavaScript Array methods on the JavaScript | |
| 62 * proxy object for the Dart List. Some of the JavaScript methods are | |
| 63 * implemented in Dart and C++ code, some are implemented in this JavaScript | |
| 64 * polyfill script. | |
| 65 */ | |
| 66 const char* dartArrayPolyfill = | |
| 67 "(function() {" | |
| 68 // If filter has already been defined on the DartList prototype, | |
| 69 // another DOM isolate has beaten us to polyfilling. | |
| 70 " if ($DartList.prototype.hasOwnProperty('filter')) return;" | |
| 71 " var isArray = Array.isArray;" | |
| 72 " var concat = Array.prototype.concat;" | |
| 73 " function makeSafeArgs(args) {" | |
| 74 " var len = args.length;" | |
| 75 " for (var i = 0; i < len; ++i) {" | |
| 76 " var arg = args[i];" | |
| 77 " if (arg instanceof $DartList) {" | |
| 78 " args[i] = arg.$toJsArray();" | |
| 79 " }" | |
| 80 " }" | |
| 81 " };" | |
| 82 | |
| 83 // If performance becomes an issue, we could implement these | |
| 84 // methods in Dart instead of creating a shallow copy JavaScript | |
| 85 // array containing the elements of the Dart List and calling | |
| 86 // the JavaScript method. | |
| 87 | |
| 88 // Handle methods that take a callback with value, index, and thisArg | |
| 89 // parameters. The trick is we need to make thisArg reference the | |
| 90 // underlying Dart List rather than the JavaScript copy we create for | |
| 91 // implementation convenience. | |
| 92 " ['filter', 'forEach', 'some', 'every', 'map'].forEach(function(name) {" | |
| 93 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va lue: function(callback, thisArg) {" | |
| 94 " var dartList = this;" | |
| 95 " return this.$toJsArray()[name](function(value, index) {" | |
| 96 " return callback.call(thisArg, value, index, dartList);" | |
| 97 " });" | |
| 98 " }});" | |
| 99 " });" | |
| 100 | |
| 101 " ['slice', 'indexOf', 'lastIndexOf'].forEach(function(name) {" | |
| 102 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va lue: function() {" | |
| 103 " var jsArray = this.$toJsArray();" | |
| 104 " return jsArray[name].apply(jsArray, arguments);" | |
| 105 " }});" | |
| 106 " });" | |
| 107 | |
| 108 " ['reduce', 'reduceRight'].forEach(function(name) {" | |
| 109 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va lue: function(callback, thisArg) {" | |
| 110 " var dartList = this;" | |
| 111 " return this.$toJsArray()[name](function(previousValue, currentValue, index) {" | |
| 112 " return callback.call(thisArg, previousValue, currentValue, index, dartList);" | |
| 113 " });" | |
| 114 " }});" | |
| 115 " });" | |
| 116 | |
| 117 // Arguments to concat that are Arrays are treated differently. | |
| 118 // Warning: this will slow down general JavaScript array concat performance in Dartium. | |
| 119 " Array.prototype.concat = function() {" | |
| 120 " makeSafeArgs(arguments);" | |
| 121 " return concat.apply(this, arguments);" | |
| 122 " };" | |
| 123 | |
| 124 // Need to make sure that Array.isArray returns true for Dart lists. | |
| 125 " Array.isArray = function(arr) {" | |
| 126 " return isArray(arr) || (arr instanceof $DartList);" | |
| 127 " };" | |
| 128 "})();"; | |
| 129 | |
| 55 static v8::Local<v8::FunctionTemplate> dartFunctionTemplate(); | 130 static v8::Local<v8::FunctionTemplate> dartFunctionTemplate(); |
| 56 static v8::Local<v8::FunctionTemplate> dartObjectTemplate(); | 131 static v8::Local<v8::FunctionTemplate> dartObjectTemplate(); |
| 132 static v8::Local<v8::FunctionTemplate> dartListTemplate(); | |
| 133 | |
| 134 // TODO(jacobr): we should really be using this method everywhere instead of | |
| 135 // sticking interop methods in the dart:html _Utils class. | |
| 136 static Dart_Handle invokeTopLevelJsInteropMethod(DartDOMData* domData, const cha r* methodName, int argCount, Dart_Handle* args) | |
| 137 { | |
| 138 Dart_PersistentHandle library = domData->jsLibrary(); | |
| 139 ASSERT(!Dart_IsError(library)); | |
| 140 return Dart_Invoke(library, Dart_NewStringFromCString(methodName), argCount, args); | |
| 141 } | |
| 57 | 142 |
| 58 template<typename CallbackInfo> | 143 template<typename CallbackInfo> |
| 59 void setJsReturnValue(DartDOMData* domData, CallbackInfo info, Dart_Handle resul t) | 144 void setJsReturnValue(DartDOMData* domData, CallbackInfo info, Dart_Handle resul t) |
| 60 { | 145 { |
| 61 if (Dart_IsError(result)) { | 146 if (Dart_IsError(result)) { |
| 62 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | 147 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 63 V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart _GetError(result)), v8Isolate); | 148 V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart _GetError(result)), v8Isolate); |
| 64 } else { | 149 } else { |
| 65 Dart_Handle exception = 0; | 150 Dart_Handle exception = 0; |
| 66 v8::Local<v8::Value> ret = JsInterop::fromDart(domData, result, exceptio n); | 151 v8::Local<v8::Value> ret = JsInterop::fromDart(domData, result, exceptio n); |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 223 if (DartUtilities::isFunction(domData, handle)) { | 308 if (DartUtilities::isFunction(domData, handle)) { |
| 224 v8::Local<v8::Object> functionProxy = dartFunctionTemplate()->InstanceTe mplate()->NewInstance(); | 309 v8::Local<v8::Object> functionProxy = dartFunctionTemplate()->InstanceTe mplate()->NewInstance(); |
| 225 DartHandleProxy::writePointerToProxy(functionProxy, handle); | 310 DartHandleProxy::writePointerToProxy(functionProxy, handle); |
| 226 // The raw functionProxy doesn't behave enough like a true JS function | 311 // The raw functionProxy doesn't behave enough like a true JS function |
| 227 // so we wrap it in a true JS function. | 312 // so we wrap it in a true JS function. |
| 228 return domData->jsInteropData()->wrapDartFunction()->Call(functionProxy, 0, 0); | 313 return domData->jsInteropData()->wrapDartFunction()->Call(functionProxy, 0, 0); |
| 229 } | 314 } |
| 230 | 315 |
| 231 v8::Local<v8::Object> proxy; | 316 v8::Local<v8::Object> proxy; |
| 232 ASSERT(Dart_IsInstance(handle)); | 317 ASSERT(Dart_IsInstance(handle)); |
| 233 proxy = dartObjectTemplate()->InstanceTemplate()->NewInstance(); | 318 // Simulate the behavior of the Dart dev compiler where new List() is |
| 319 // equivalent to a JavaScript array. We accomplish this by creating a | |
| 320 // JavaScript object that fakes that it is a JavaScript array but is | |
| 321 // actually backed by a Dart list. This is not a breaking change as | |
| 322 // existing Dart-JS interop passed arrays as opaque Dart handles. | |
| 323 // The jsify method can still be called if you wish to create a copy | |
| 324 // of a json like Dart data structure. | |
| 325 if (Dart_IsList(handle)) { | |
| 326 proxy = dartListTemplate()->InstanceTemplate()->NewInstance(); | |
| 327 } else { | |
| 328 proxy = dartObjectTemplate()->InstanceTemplate()->NewInstance(); | |
| 329 } | |
| 234 DartHandleProxy::writePointerToProxy(proxy, handle); | 330 DartHandleProxy::writePointerToProxy(proxy, handle); |
| 235 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | 331 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 236 proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::B oolean::New(v8Isolate, true)); | 332 proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::B oolean::New(v8Isolate, true)); |
| 237 | 333 |
| 238 return proxy; | 334 return proxy; |
| 239 } | 335 } |
| 240 | 336 |
| 241 JsObject::JsObject(v8::Local<v8::Object> v8Handle) | 337 JsObject::JsObject(v8::Local<v8::Object> v8Handle) |
| 242 { | 338 { |
| 243 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | 339 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 293 | 389 |
| 294 if (object->IsArray()) { | 390 if (object->IsArray()) { |
| 295 RefPtr<JsArray> jsArray = JsArray::create(object.As<v8::Array>()); | 391 RefPtr<JsArray> jsArray = JsArray::create(object.As<v8::Array>()); |
| 296 return JsArray::toDart(jsArray); | 392 return JsArray::toDart(jsArray); |
| 297 } | 393 } |
| 298 | 394 |
| 299 RefPtr<JsObject> jsObject = JsObject::create(object); | 395 RefPtr<JsObject> jsObject = JsObject::create(object); |
| 300 return JsObject::toDart(jsObject); | 396 return JsObject::toDart(jsObject); |
| 301 } | 397 } |
| 302 | 398 |
| 399 static void maybeCreateJsObjectImplClass(DartDOMData* domData) | |
| 400 { | |
| 401 DartJsInteropData* interopData = domData->jsInteropData(); | |
| 402 // Skip if the JSObjectImpl class has already been defined. | |
| 403 if (interopData->jsObjectImplDefined()) { | |
| 404 return; | |
| 405 } | |
| 406 // Helper method that generates boilerplate source code for | |
| 407 // JsObjectImpl, JsFunctionImpl, and JsArrayImpl classes that implement | |
| 408 // all Dart types that have been passed to the dart:js registerJsInterfaces | |
| 409 // method. The sole purpose of these classes is to ensure that checked mode | |
| 410 // allows casting a JsObject to all types implemented by a JsObject. | |
| 411 Dart_Handle source = invokeTopLevelJsInteropMethod(domData, "_generateJsObje ctImplPart", 0, 0); | |
| 412 ASSERT(Dart_IsString(source)); | |
| 413 | |
| 414 Dart_Handle ALLOW_UNUSED ret = Dart_LibraryLoadPatch(domData->jsLibrary(), D art_NewStringFromCString("JsInteropImpl.dart"), source); | |
| 415 ASSERT(!Dart_IsError(ret)); | |
| 416 ret = Dart_FinalizeLoading(false); | |
| 417 ASSERT(!Dart_IsError(ret)); | |
| 418 | |
| 419 interopData->setJsObjectImplDefined(); | |
| 420 | |
| 421 // Start of polyfill work to make Dart List proxies behave like JavaScript | |
| 422 // Arrays by monkey patching JavaScript Array and the List JavaScript | |
| 423 // proxies as needed. | |
| 424 v8::Context::Scope scope(DartUtilities::currentV8Context()); | |
| 425 | |
| 426 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 427 | |
| 428 v8::Local<v8::Function> dartArrayConstructor = dartListTemplate()->GetFuncti on(); | |
| 429 | |
| 430 DartUtilities::currentV8Context()->Global()->Set(v8::String::NewFromUtf8(v8I solate, "$DartList"), | |
| 431 dartArrayConstructor); | |
| 432 V8ScriptRunner::compileAndRunInternalScript(v8::String::NewFromUtf8(v8Isolat e, dartArrayPolyfill), v8Isolate); | |
| 433 } | |
| 434 | |
| 303 Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject) | 435 Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject) |
| 304 { | 436 { |
| 305 return DartDOMWrapper::createWrapper<JsObject>(DartDOMData::current(), jsObj ect.get(), JsObject::dartClassId); | 437 DartDOMData* domData = DartDOMData::current(); |
| 438 // We need to ensure JsObjectImpl exists before creating the wrapper. | |
| 439 maybeCreateJsObjectImplClass(domData); | |
| 440 return DartDOMWrapper::createWrapper<JsObject>(domData, jsObject.get(), JsOb ject::dartClassId); | |
| 306 } | 441 } |
| 307 | 442 |
| 308 JsObject::~JsObject() | 443 JsObject::~JsObject() |
| 309 { | 444 { |
| 310 v8Object.Reset(); | 445 v8Object.Reset(); |
| 311 } | 446 } |
| 312 | 447 |
| 313 Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) | 448 Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) |
| 314 { | 449 { |
| 315 return DartDOMWrapper::createWrapper<JsFunction>(DartDOMData::current(), jsF unction.get(), JsFunction::dartClassId); | 450 DartDOMData* domData = DartDOMData::current(); |
| 451 // We need to ensure JsObjectImpl exists before creating the wrapper. | |
| 452 maybeCreateJsObjectImplClass(domData); | |
| 453 return DartDOMWrapper::createWrapper<JsFunction>(domData, jsFunction.get(), JsFunction::dartClassId); | |
| 316 } | 454 } |
| 317 | 455 |
| 318 JsFunction::JsFunction(v8::Local<v8::Function> v8Handle) : JsObject(v8Handle) { } | 456 JsFunction::JsFunction(v8::Local<v8::Function> v8Handle) : JsObject(v8Handle) { } |
| 319 | 457 |
| 320 PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle) | 458 PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle) |
| 321 { | 459 { |
| 322 return adoptRef(new JsFunction(v8Handle)); | 460 return adoptRef(new JsFunction(v8Handle)); |
| 323 } | 461 } |
| 324 | 462 |
| 325 v8::Local<v8::Function> JsFunction::localV8Function() | 463 v8::Local<v8::Function> JsFunction::localV8Function() |
| 326 { | 464 { |
| 327 return localV8Object().As<v8::Function>(); | 465 return localV8Object().As<v8::Function>(); |
| 328 } | 466 } |
| 329 | 467 |
| 330 Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) | 468 Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) |
| 331 { | 469 { |
| 332 return DartDOMWrapper::createWrapper<JsArray>(DartDOMData::current(), jsArra y.get(), JsArray::dartClassId); | 470 DartDOMData* domData = DartDOMData::current(); |
| 471 // We need to ensure JsArrayImpl exists before creating the wrapper. | |
| 472 maybeCreateJsObjectImplClass(domData); | |
| 473 return DartDOMWrapper::createWrapper<JsArray>(domData, jsArray.get(), JsArra y::dartClassId); | |
| 333 } | 474 } |
| 334 | 475 |
| 335 JsArray::JsArray(v8::Local<v8::Array> v8Handle) : JsObject(v8Handle) { } | 476 JsArray::JsArray(v8::Local<v8::Array> v8Handle) : JsObject(v8Handle) { } |
| 336 | 477 |
| 337 PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Array> v8Handle) | 478 PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Array> v8Handle) |
| 338 { | 479 { |
| 339 return adoptRef(new JsArray(v8Handle)); | 480 return adoptRef(new JsArray(v8Handle)); |
| 340 } | 481 } |
| 341 | 482 |
| 342 v8::Local<v8::Array> JsArray::localV8Array() | 483 v8::Local<v8::Array> JsArray::localV8Array() |
| (...skipping 522 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 865 ASSERT_NOT_REACHED(); | 1006 ASSERT_NOT_REACHED(); |
| 866 } | 1007 } |
| 867 | 1008 |
| 868 static void contextCallback(Dart_NativeArguments args) | 1009 static void contextCallback(Dart_NativeArguments args) |
| 869 { | 1010 { |
| 870 v8::Local<v8::Context> v8Context = DartUtilities::currentV8Context(); | 1011 v8::Local<v8::Context> v8Context = DartUtilities::currentV8Context(); |
| 871 v8::Context::Scope scope(v8Context); | 1012 v8::Context::Scope scope(v8Context); |
| 872 Dart_SetReturnValue(args, JsObject::toDart(v8Context->Global())); | 1013 Dart_SetReturnValue(args, JsObject::toDart(v8Context->Global())); |
| 873 } | 1014 } |
| 874 | 1015 |
| 1016 static void finalizeJsInterfacesCallback(Dart_NativeArguments args) | |
| 1017 { | |
| 1018 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(a rgs)); | |
| 1019 maybeCreateJsObjectImplClass(domData); | |
| 1020 } | |
| 1021 | |
| 1022 static void interfacesFinalizedCallback(Dart_NativeArguments args) | |
| 1023 { | |
| 1024 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(a rgs)); | |
| 1025 DartJsInteropData* interopData = domData->jsInteropData(); | |
| 1026 // Skip if the JSObjectImpl class has already been defined. | |
| 1027 Dart_SetBooleanReturnValue(args, interopData->jsObjectImplDefined()); | |
| 1028 } | |
| 1029 | |
| 875 v8::Handle<v8::Value> mapToV8(DartDOMData* domData, Dart_Handle value, DartHandl eToV8Map& map, Dart_Handle& exception) | 1030 v8::Handle<v8::Value> mapToV8(DartDOMData* domData, Dart_Handle value, DartHandl eToV8Map& map, Dart_Handle& exception) |
| 876 { | 1031 { |
| 877 Dart_Handle asList = DartUtilities::invokeUtilsMethod("convertMapToList", 1, &value); | 1032 Dart_Handle asList = DartUtilities::invokeUtilsMethod("convertMapToList", 1, &value); |
| 878 if (!DartUtilities::checkResult(asList, exception)) | 1033 if (!DartUtilities::checkResult(asList, exception)) |
| 879 return v8::Handle<v8::Value>(); | 1034 return v8::Handle<v8::Value>(); |
| 880 ASSERT(Dart_IsList(asList)); | 1035 ASSERT(Dart_IsList(asList)); |
| 881 | 1036 |
| 882 // Now we have a list [key, value, key, value, ....], create a v8 object and set necesary | 1037 // Now we have a list [key, value, key, value, ....], create a v8 object and set necesary |
| 883 // properties on it. | 1038 // properties on it. |
| 884 v8::Handle<v8::Object> object = v8::Object::New(v8::Isolate::GetCurrent()); | 1039 v8::Handle<v8::Object> object = v8::Object::New(v8::Isolate::GetCurrent()); |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1003 fail: | 1158 fail: |
| 1004 Dart_ThrowException(exception); | 1159 Dart_ThrowException(exception); |
| 1005 ASSERT_NOT_REACHED(); | 1160 ASSERT_NOT_REACHED(); |
| 1006 } | 1161 } |
| 1007 | 1162 |
| 1008 } | 1163 } |
| 1009 | 1164 |
| 1010 static DartNativeEntry nativeEntries[] = { | 1165 static DartNativeEntry nativeEntries[] = { |
| 1011 { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCa llback" }, | 1166 { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCa llback" }, |
| 1012 { JsInteropInternal::contextCallback, 0, "Js_context_Callback" }, | 1167 { JsInteropInternal::contextCallback, 0, "Js_context_Callback" }, |
| 1168 { JsInteropInternal::finalizeJsInterfacesCallback, 0, "Js_finalizeJsInterfac es" }, | |
| 1169 { JsInteropInternal::interfacesFinalizedCallback, 0, "Js_interfacesFinalized _Callback" }, | |
| 1013 { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, | 1170 { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, |
| 1014 { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, | 1171 { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, |
| 1015 { JsInteropInternal::getterCallback, 2, "JsObject_[]" }, | 1172 { JsInteropInternal::getterCallback, 2, "JsObject_[]" }, |
| 1016 { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, | 1173 { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, |
| 1017 { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, | 1174 { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, |
| 1018 { JsInteropInternal::callMethodCallback, 3, "JsObject_callMethod" }, | 1175 { JsInteropInternal::callMethodCallback, 3, "JsObject_callMethod" }, |
| 1019 { JsInteropInternal::toStringCallback, 1, "JsObject_toString" }, | 1176 { JsInteropInternal::toStringCallback, 1, "JsObject_toString" }, |
| 1020 { JsInteropInternal::identityEqualityCallback, 2, "JsObject_identityEquality " }, | 1177 { JsInteropInternal::identityEqualityCallback, 2, "JsObject_identityEquality " }, |
| 1021 { JsInteropInternal::hasPropertyCallback, 2, "JsObject_hasProperty" }, | 1178 { JsInteropInternal::hasPropertyCallback, 2, "JsObject_hasProperty" }, |
| 1022 { JsInteropInternal::deletePropertyCallback, 2, "JsObject_deleteProperty" }, | 1179 { JsInteropInternal::deletePropertyCallback, 2, "JsObject_deleteProperty" }, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 1048 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) | 1205 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) |
| 1049 { | 1206 { |
| 1050 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { | 1207 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { |
| 1051 if (nf == nativeEntries[i].nativeFunction) { | 1208 if (nf == nativeEntries[i].nativeFunction) { |
| 1052 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); | 1209 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); |
| 1053 } | 1210 } |
| 1054 } | 1211 } |
| 1055 return 0; | 1212 return 0; |
| 1056 } | 1213 } |
| 1057 | 1214 |
| 1058 } | 1215 // Methods enabling a Dart List to emulate a JS Array. |
| 1216 static void indexedGetterArray(uint32_t index, const v8::PropertyCallbackInfo<v8 ::Value>& info) | |
| 1217 { | |
| 1218 DartScopes scopes(info.Holder()); | |
| 1219 Dart_Handle handle = scopes.handle; | |
| 1220 DartDOMData* domData = DartDOMData::current(); | |
| 1221 Dart_Handle ret = 0; | |
| 1222 ASSERT(Dart_IsList(handle)); | |
| 1223 ret = Dart_ListGetAt(handle, index); | |
| 1224 if (Dart_IsError(ret)) { | |
| 1225 // Return undefined if the index is invalid to match JS semantics. | |
| 1226 // TODO(jacobr): we should log to the console warning of bad use of a | |
| 1227 // Dart List. | |
| 1228 return; | |
| 1229 } | |
| 1230 setJsReturnValue(domData, info, ret); | |
| 1231 } | |
| 1232 | |
| 1233 static void indexedSetterArray(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) | |
| 1234 { | |
| 1235 DartScopes scopes(info.Holder()); | |
| 1236 Dart_Handle handle = scopes.handle; | |
| 1237 DartDOMData* domData = DartDOMData::current(); | |
| 1238 | |
| 1239 Dart_Handle ret = 0; | |
| 1240 ASSERT(Dart_IsList(handle)); | |
| 1241 intptr_t length = 0; | |
| 1242 ret = Dart_ListLength(handle, &length); | |
| 1243 ASSERT(!Dart_IsError(ret)); | |
| 1244 if (index >= length) { | |
| 1245 // Approximate JS semantics by filling the list with nulls if we | |
| 1246 // attempt to add an element past the end of the list. | |
| 1247 // TODO(jacobr): ideally we would match JS semantics exactly and fill | |
| 1248 // the list with undefined. | |
| 1249 Dart_Handle args[2] = { handle, DartUtilities::unsignedToDart(index + 1) }; | |
| 1250 ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_arra yExtend"), 2, args); | |
| 1251 ASSERT(!Dart_IsError(ret)); | |
| 1252 } | |
| 1253 ret = Dart_ListSetAt(handle, index, DartHandleProxy::unwrapValue(value)); | |
| 1254 if (Dart_IsError(ret)) { | |
| 1255 // Return undefined if the index is invalid to match JS semantics. | |
| 1256 // TODO(jacobr): we should log to the console warning of bad use of a | |
| 1257 // Dart list or add a JS expando to the object ala JS. | |
| 1258 } | |
| 1259 setJsReturnValue(domData, info, ret); | |
| 1260 } | |
| 1261 | |
| 1262 static void indexedEnumeratorArray(const v8::PropertyCallbackInfo<v8::Array>& in fo) | |
| 1263 { | |
| 1264 DartScopes scopes(info.Holder()); | |
| 1265 Dart_Handle handle = scopes.handle; | |
| 1266 | |
| 1267 intptr_t length = 0; | |
| 1268 ASSERT(Dart_IsList(handle)); | |
| 1269 Dart_ListLength(handle, &length); | |
| 1270 | |
| 1271 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1272 v8::Local<v8::Array> indexes = v8::Array::New(v8Isolate, length); | |
| 1273 for (int i = 0; i < length; i++) | |
| 1274 indexes->Set(i, v8::Integer::New(v8Isolate, i)); | |
| 1275 | |
| 1276 v8SetReturnValue(info, indexes); | |
| 1277 } | |
| 1278 | |
| 1279 v8::Handle<v8::Array> shallowListToV8(Dart_Handle value, DartDOMData* domData, D art_Handle& exception) | |
| 1280 { | |
| 1281 ASSERT(Dart_IsList(value)); | |
| 1282 | |
| 1283 intptr_t length = 0; | |
| 1284 Dart_Handle result = Dart_ListLength(value, &length); | |
| 1285 if (!DartUtilities::checkResult(result, exception)) | |
| 1286 return v8::Handle<v8::Array>(); | |
| 1287 | |
| 1288 v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), lengt h); | |
| 1289 | |
| 1290 for (intptr_t i = 0; i < length; ++i) { | |
| 1291 result = Dart_ListGetAt(value, i); | |
| 1292 v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result, exc eption); | |
| 1293 // TODO(jacobr): is there a better way to handle this error case? | |
| 1294 if (exception) | |
| 1295 return v8::Handle<v8::Array>(); | |
| 1296 array->Set(i, v8value); | |
| 1297 } | |
| 1298 return array; | |
| 1299 } | |
| 1300 | |
| 1301 // If one of the methods we added to the Dart List proxy to make it mascarade | |
| 1302 // as JavaScript Array happens to get called on a JavaScript object instead of | |
| 1303 // a Dart List proxy object we use the corresponding regular JavaScript Array | |
| 1304 // method which will do something plausible in most cases even if the | |
| 1305 // JavaScript object isn't actually a JavaScript array. | |
| 1306 bool handleNonDartProxyThis(const v8::FunctionCallbackInfo<v8::Value>& info, con st char* jsMethodName) | |
| 1307 { | |
| 1308 if (DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1309 return false; | |
| 1310 } | |
| 1311 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
|
vsm
2015/06/29 13:49:26
Do we actually ever get down this path? And if so
Jacob
2015/06/29 17:56:08
Yeah it is a bit strange but I'm trying to make th
| |
| 1312 // Get the method on JS array in an inefficient way. | |
| 1313 v8::Local<v8::Function> method = v8::Array::New(v8Isolate)->Get(v8::String:: NewFromUtf8(v8Isolate, jsMethodName)).As<v8::Function>(); | |
| 1314 ASSERT(!method.IsEmpty()); | |
| 1315 int length = info.Length(); | |
| 1316 Vector<v8::Local<v8::Value> > v8Args(length); | |
| 1317 for (int i = 0; i < length; ++i) { | |
| 1318 v8Args[i] = info[i]; | |
| 1319 } | |
| 1320 v8SetReturnValue(info, method->Call(info.Holder(), length, v8Args.data())); | |
| 1321 return true; | |
| 1322 } | |
| 1323 | |
| 1324 void arrayHelper(const v8::FunctionCallbackInfo<v8::Value>& info, const char* me thodName, const char* jsMethodName) | |
| 1325 { | |
| 1326 if (handleNonDartProxyThis(info, jsMethodName)) { | |
| 1327 return; | |
| 1328 } | |
| 1329 DartScopes scopes(info.Holder()); | |
| 1330 DartDOMData* domData = DartDOMData::current(); | |
| 1331 Dart_Handle handle = scopes.handle; | |
| 1332 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 1, &handle); | |
| 1333 setJsReturnValue(domData, info, ret); | |
| 1334 } | |
| 1335 | |
| 1336 void arrayHelper1Arg(const v8::FunctionCallbackInfo<v8::Value>& info, const char * methodName, const char* jsMethodName) | |
| 1337 { | |
| 1338 if (handleNonDartProxyThis(info, jsMethodName)) { | |
| 1339 return; | |
| 1340 } | |
| 1341 DartScopes scopes(info.Holder()); | |
| 1342 DartDOMData* domData = DartDOMData::current(); | |
| 1343 Dart_Handle handle = scopes.handle; | |
| 1344 Dart_Handle e; | |
| 1345 if (info.Length() == 0) { | |
| 1346 e = Dart_Null(); | |
| 1347 } else { | |
| 1348 e = JsInterop::toDart(info[0]); | |
| 1349 } | |
| 1350 Dart_Handle args[2] = { handle, e }; | |
| 1351 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 2, args); | |
| 1352 setJsReturnValue(domData, info, ret); | |
| 1353 } | |
| 1354 | |
| 1355 void arrayHelperWithArgsAsList(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName, const char* jsMethodName) | |
| 1356 { | |
| 1357 if (handleNonDartProxyThis(info, jsMethodName)) { | |
| 1358 return; | |
| 1359 } | |
| 1360 DartScopes scopes(info.Holder()); | |
| 1361 DartDOMData* domData = DartDOMData::current(); | |
| 1362 Dart_Handle handle = scopes.handle; | |
| 1363 int length = info.Length(); | |
| 1364 Dart_Handle argsList = Dart_NewList(length); | |
| 1365 for (int i = 0; i < length; ++i) { | |
| 1366 Dart_ListSetAt(argsList, i, JsInterop::toDart(info[i])); | |
| 1367 } | |
| 1368 // Note: this is also just info.Holder(). | |
| 1369 Dart_Handle args[2] = { handle, argsList }; | |
| 1370 setJsReturnValue(domData, info, Dart_Invoke(domData->jsLibrary(), Dart_NewSt ringFromCString(methodName), 2, args)); | |
| 1371 } | |
| 1372 | |
| 1373 static void arrayNamedPropertyGetter(v8::Local<v8::String> name, const v8::Prope rtyCallbackInfo<v8::Value>& info) | |
| 1374 { | |
| 1375 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1376 // I don't think this case can occur but avoid crashing if there is an | |
| 1377 // exotic way to trigger it. | |
| 1378 return; | |
| 1379 } | |
| 1380 | |
| 1381 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1382 if (!name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) | |
| 1383 return; | |
| 1384 | |
| 1385 DartScopes scopes(info.Holder()); | |
| 1386 Dart_Handle handle = scopes.handle; | |
| 1387 | |
| 1388 intptr_t length = 0; | |
| 1389 Dart_ListLength(handle, &length); | |
| 1390 v8SetReturnValueInt(info, length); | |
| 1391 } | |
| 1392 | |
| 1393 static void arrayNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v 8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) | |
| 1394 { | |
| 1395 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1396 // I don't think this case can occur but avoid crashing if there is an | |
| 1397 // exotic way to trigger it. | |
| 1398 return; | |
| 1399 } | |
| 1400 | |
| 1401 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1402 if (!property->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
| 1403 return; | |
| 1404 } | |
| 1405 | |
| 1406 DartScopes scopes(info.Holder()); | |
| 1407 Dart_Handle handle = scopes.handle; | |
| 1408 DartDOMData* domData = DartDOMData::current(); | |
| 1409 Dart_Handle args[2] = { handle, JsInterop::toDart(value) }; | |
| 1410 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g("_setListLength"), 2, args); | |
| 1411 setJsReturnValue(domData, info, ret); | |
| 1412 } | |
| 1413 | |
| 1414 static void arrayQueryProperty(v8::Local<v8::String> name, const v8::PropertyCal lbackInfo<v8::Integer>& info) | |
| 1415 { | |
| 1416 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1417 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
| 1418 v8SetReturnValueInt(info, v8::DontEnum | v8::DontDelete); | |
| 1419 } | |
| 1420 } | |
| 1421 | |
| 1422 static void arrayDeleteProperty(v8::Local<v8::String> name, const v8::PropertyCa llbackInfo<v8::Boolean>& info) | |
| 1423 { | |
| 1424 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1425 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
| 1426 v8SetReturnValueBool(info, false); | |
| 1427 } | |
| 1428 } | |
| 1429 | |
| 1430 void arrayToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1431 { | |
| 1432 arrayHelper(info, "_arrayToString", "toString"); | |
| 1433 } | |
| 1434 | |
| 1435 | |
| 1436 void arrayJoinCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1437 { | |
| 1438 arrayHelper1Arg(info, "_arrayJoin", "join"); | |
| 1439 } | |
| 1440 | |
| 1441 void arrayPushCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1442 { | |
| 1443 arrayHelperWithArgsAsList(info, "_arrayPush", "push"); | |
| 1444 } | |
| 1445 | |
| 1446 void arrayPopCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1447 { | |
| 1448 arrayHelper(info, "_arrayPop", "pop"); | |
| 1449 } | |
| 1450 | |
| 1451 void concatCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1452 { | |
| 1453 arrayHelperWithArgsAsList(info, "_arrayConcat", "concat"); | |
| 1454 } | |
| 1455 | |
| 1456 void arrayReverseCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1457 { | |
| 1458 arrayHelper(info, "_arrayReverse", "reverse"); | |
| 1459 } | |
| 1460 | |
| 1461 void arrayShiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1462 { | |
| 1463 arrayHelper(info, "_arrayShift", "shift"); | |
| 1464 } | |
| 1465 | |
| 1466 void arrayUnshiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1467 { | |
| 1468 arrayHelperWithArgsAsList(info, "_arrayUnshift", "unshift"); | |
| 1469 } | |
| 1470 | |
| 1471 void arraySpliceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1472 { | |
| 1473 arrayHelperWithArgsAsList(info, "_arraySplice", "splice"); | |
| 1474 } | |
| 1475 | |
| 1476 void toJsArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1477 { | |
| 1478 // This isn't a method on the JavaScript Array class so it is fine to | |
| 1479 // just return the existing hopefully Array like JS object if not called on | |
| 1480 // a Dart List. | |
| 1481 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1482 v8SetReturnValue(info, info.Holder()); | |
| 1483 } | |
| 1484 DartScopes scopes(info.Holder()); | |
| 1485 DartDOMData* domData = DartDOMData::current(); | |
| 1486 Dart_Handle handle = scopes.handle; | |
| 1487 Dart_Handle exception = 0; | |
| 1488 v8::Local<v8::Array> v8Array = shallowListToV8(handle, domData, exception); | |
| 1489 ASSERT(!exception); | |
| 1490 v8SetReturnValue(info, v8Array); | |
| 1491 } | |
| 1492 | |
| 1493 void arraySortCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1494 { | |
| 1495 // TODO(jacobr): consider using the JavaScript sort method instead. | |
| 1496 arrayHelper1Arg(info, "_arraySort", "sort"); | |
| 1497 } | |
| 1498 | |
| 1499 v8::Local<v8::FunctionTemplate> dartListTemplate() | |
| 1500 { | |
| 1501 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()) ; | |
| 1502 v8::Local<v8::FunctionTemplate> proxyTemplateLocal; | |
| 1503 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1504 if (proxyTemplate.IsEmpty()) { | |
| 1505 proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New (v8Isolate)); | |
| 1506 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate); | |
| 1507 // Set to Array because we want these instances to appear to be | |
| 1508 // JavaScript arrays as far as user code is concerned. | |
| 1509 proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Arr ay")); | |
| 1510 // Hack to set the prototype to be the prototype of an actual JavaScript Array. | |
| 1511 | |
| 1512 v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->Proto typeTemplate(); | |
| 1513 | |
| 1514 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8:: FunctionTemplate::New(v8Isolate, arrayToStringCallback), v8::DontEnum); | |
| 1515 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "join"), v8::Func tionTemplate::New(v8Isolate, arrayJoinCallback), v8::DontEnum); | |
| 1516 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "push"), v8::Func tionTemplate::New(v8Isolate, arrayPushCallback), v8::DontEnum); | |
| 1517 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "pop"), v8::Funct ionTemplate::New(v8Isolate, arrayPopCallback), v8::DontEnum); | |
| 1518 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "concat"), v8::Fu nctionTemplate::New(v8Isolate, concatCallback), v8::DontEnum); | |
| 1519 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "reverse"), v8::F unctionTemplate::New(v8Isolate, arrayReverseCallback), v8::DontEnum); | |
| 1520 | |
| 1521 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "shift"), v8::Fun ctionTemplate::New(v8Isolate, arrayShiftCallback), v8::DontEnum); | |
| 1522 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "unshift"), v8::F unctionTemplate::New(v8Isolate, arrayUnshiftCallback), v8::DontEnum); | |
| 1523 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "splice"), v8::Fu nctionTemplate::New(v8Isolate, arraySpliceCallback), v8::DontEnum); | |
| 1524 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "sort"), v8::Func tionTemplate::New(v8Isolate, arraySortCallback), v8::DontEnum); | |
| 1525 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "$toJsArray"), v8 ::FunctionTemplate::New(v8Isolate, toJsArrayCallback), v8::DontEnum); | |
| 1526 | |
| 1527 // ES6 experimental properties not currently supported that we could sup port if needed. | |
| 1528 // These would require building separate live proxy objects. | |
| 1529 // "entries", | |
| 1530 // "values", | |
| 1531 // "keys" | |
| 1532 | |
| 1533 v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(p roxyTemplateLocal); | |
| 1534 instanceTemplate->SetIndexedPropertyHandler(&indexedGetterArray, &indexe dSetterArray, 0, 0, &indexedEnumeratorArray); | |
| 1535 instanceTemplate->SetNamedPropertyHandler(&arrayNamedPropertyGetter, &ar rayNamedPropertySetter, &arrayQueryProperty, &arrayDeleteProperty, 0); | |
| 1536 } else { | |
| 1537 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate); | |
| 1538 } | |
| 1539 return proxyTemplateLocal; | |
| 1540 } | |
| 1541 | |
| 1542 } | |
| OLD | NEW |