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 void arrayHelper(const v8::FunctionCallbackInfo<v8::Value>& info, const char* me thodName) | |
| 1302 { | |
| 1303 // We need to bail out if "this" is not a Dart proxy or we will crash the VM . | |
| 1304 // TODO(jacob): we could generate a user readable error message instead of | |
| 1305 // just returning. | |
| 1306 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1307 return; | |
| 1308 } | |
| 1309 DartScopes scopes(info.Holder()); | |
| 1310 DartDOMData* domData = DartDOMData::current(); | |
| 1311 Dart_Handle handle = scopes.handle; | |
| 1312 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 1, &handle); | |
| 1313 setJsReturnValue(domData, info, ret); | |
| 1314 } | |
| 1315 | |
| 1316 void arrayHelper1Arg(const v8::FunctionCallbackInfo<v8::Value>& info, const char * methodName) | |
| 1317 { | |
| 1318 // We need to bail out if "this" is not a Dart proxy or we will crash the VM . | |
| 1319 // TODO(jacob): we could generate a user readable error message instead of | |
| 1320 // just returning. | |
|
vsm
2015/06/26 14:46:33
Throw an exception? Should be able to steal the b
Jacob
2015/06/26 20:53:30
I shouldn't throw an exception here. Added some so
| |
| 1321 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1322 return; | |
| 1323 } | |
| 1324 DartScopes scopes(info.Holder()); | |
| 1325 DartDOMData* domData = DartDOMData::current(); | |
| 1326 Dart_Handle handle = scopes.handle; | |
| 1327 Dart_Handle e; | |
| 1328 if (info.Length() == 0) { | |
| 1329 e = Dart_Null(); | |
| 1330 } else { | |
| 1331 e = JsInterop::toDart(info[0]); | |
| 1332 } | |
| 1333 Dart_Handle args[2] = { handle, e }; | |
| 1334 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 2, args); | |
| 1335 setJsReturnValue(domData, info, ret); | |
| 1336 } | |
| 1337 | |
| 1338 static void arrayNamedPropertyGetter(v8::Local<v8::String> name, const v8::Prope rtyCallbackInfo<v8::Value>& info) | |
| 1339 { | |
| 1340 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1341 return; | |
| 1342 } | |
| 1343 | |
| 1344 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1345 if (!name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) | |
| 1346 return; | |
| 1347 | |
| 1348 DartScopes scopes(info.Holder()); | |
| 1349 Dart_Handle handle = scopes.handle; | |
| 1350 | |
| 1351 intptr_t length = 0; | |
| 1352 Dart_ListLength(handle, &length); | |
| 1353 v8SetReturnValueInt(info, length); | |
| 1354 } | |
| 1355 | |
| 1356 static void arrayNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v 8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) | |
| 1357 { | |
| 1358 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1359 return; | |
| 1360 } | |
| 1361 | |
| 1362 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1363 if (!property->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
| 1364 return; | |
| 1365 } | |
| 1366 | |
| 1367 DartScopes scopes(info.Holder()); | |
| 1368 Dart_Handle handle = scopes.handle; | |
| 1369 DartDOMData* domData = DartDOMData::current(); | |
| 1370 Dart_Handle args[2] = { handle, JsInterop::toDart(value) }; | |
| 1371 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g("_setListLength"), 2, args); | |
| 1372 setJsReturnValue(domData, info, ret); | |
| 1373 } | |
| 1374 | |
| 1375 static void arrayQueryProperty(v8::Local<v8::String> name, const v8::PropertyCal lbackInfo<v8::Integer>& info) | |
| 1376 { | |
| 1377 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1378 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
| 1379 v8SetReturnValueInt(info, v8::DontEnum | v8::DontDelete); | |
| 1380 } | |
| 1381 } | |
| 1382 | |
| 1383 static void arrayDeleteProperty(v8::Local<v8::String> name, const v8::PropertyCa llbackInfo<v8::Boolean>& info) | |
| 1384 { | |
| 1385 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1386 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { | |
| 1387 v8SetReturnValueBool(info, false); | |
| 1388 } | |
| 1389 } | |
| 1390 | |
| 1391 void arrayToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1392 { | |
| 1393 arrayHelper(info, "_arrayToString"); | |
| 1394 } | |
| 1395 | |
| 1396 | |
| 1397 void arrayJoinCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1398 { | |
| 1399 arrayHelper1Arg(info, "_arrayJoin"); | |
| 1400 } | |
| 1401 | |
| 1402 void arrayPushCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1403 { | |
| 1404 arrayHelper1Arg(info, "_arrayPush"); | |
| 1405 } | |
| 1406 | |
| 1407 void arrayPopCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1408 { | |
| 1409 arrayHelper(info, "_arrayPop"); | |
| 1410 } | |
| 1411 | |
| 1412 void arrayHelperWithArgsAsList(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName) | |
| 1413 { | |
| 1414 // We need to bail out if "this" is not a Dart proxy or we will crash the VM . | |
| 1415 // TODO(jacob): we could generate a user readable error message instead of | |
| 1416 // just returning. | |
| 1417 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1418 return; | |
| 1419 } | |
| 1420 DartScopes scopes(info.Holder()); | |
| 1421 DartDOMData* domData = DartDOMData::current(); | |
| 1422 Dart_Handle handle = scopes.handle; | |
| 1423 int length = info.Length(); | |
| 1424 Dart_Handle argsList = Dart_NewList(length); | |
| 1425 for (int i = 0; i < length; ++i) { | |
| 1426 Dart_ListSetAt(argsList, i, JsInterop::toDart(info[i])); | |
| 1427 } | |
| 1428 // Note: this is also just info.Holder(). | |
| 1429 Dart_Handle args[2] = { handle, argsList }; | |
| 1430 setJsReturnValue(domData, info, Dart_Invoke(domData->jsLibrary(), Dart_NewSt ringFromCString(methodName), 2, args)); | |
| 1431 } | |
| 1432 | |
| 1433 void concatCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1434 { | |
| 1435 arrayHelperWithArgsAsList(info, "_arrayConcat"); | |
| 1436 } | |
| 1437 | |
| 1438 void arrayReverseCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1439 { | |
| 1440 arrayHelper(info, "_arrayReverse"); | |
| 1441 } | |
| 1442 | |
| 1443 void arrayShiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1444 { | |
| 1445 arrayHelper(info, "_arrayShift"); | |
| 1446 } | |
| 1447 | |
| 1448 void arrayUnshiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1449 { | |
| 1450 arrayHelperWithArgsAsList(info, "_arrayUnshift"); | |
| 1451 } | |
| 1452 | |
| 1453 void arraySpliceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1454 { | |
| 1455 arrayHelperWithArgsAsList(info, "_arraySplice"); | |
| 1456 } | |
| 1457 | |
| 1458 void toJsArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1459 { | |
| 1460 if (!DartHandleProxy::isDartProxy(info.Holder())) { | |
| 1461 return; | |
| 1462 } | |
| 1463 DartScopes scopes(info.Holder()); | |
| 1464 DartDOMData* domData = DartDOMData::current(); | |
| 1465 Dart_Handle handle = scopes.handle; | |
| 1466 Dart_Handle exception = 0; | |
| 1467 v8::Local<v8::Array> v8Array = shallowListToV8(handle, domData, exception); | |
| 1468 ASSERT(!exception); | |
| 1469 v8SetReturnValue(info, v8Array); | |
| 1470 } | |
| 1471 | |
| 1472 void arraySortCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
| 1473 { | |
| 1474 // TODO(jacobr): consider using the JavaScript sort method instead. | |
| 1475 arrayHelper1Arg(info, "_arraySort"); | |
| 1476 } | |
| 1477 | |
| 1478 v8::Local<v8::FunctionTemplate> dartListTemplate() | |
| 1479 { | |
| 1480 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()) ; | |
| 1481 v8::Local<v8::FunctionTemplate> proxyTemplateLocal; | |
| 1482 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); | |
| 1483 if (proxyTemplate.IsEmpty()) { | |
| 1484 proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New (v8Isolate)); | |
| 1485 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate); | |
| 1486 // Set to Array because we want these instances to appear to be | |
| 1487 // JavaScript arrays as far as user code is concerned. | |
| 1488 proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Arr ay")); | |
| 1489 // Hack to set the prototype to be the prototype of an actual JavaScript Array. | |
| 1490 | |
| 1491 v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->Proto typeTemplate(); | |
| 1492 | |
| 1493 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8:: FunctionTemplate::New(v8Isolate, arrayToStringCallback), v8::DontEnum); | |
| 1494 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "join"), v8::Func tionTemplate::New(v8Isolate, arrayJoinCallback), v8::DontEnum); | |
| 1495 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "push"), v8::Func tionTemplate::New(v8Isolate, arrayPushCallback), v8::DontEnum); | |
| 1496 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "pop"), v8::Funct ionTemplate::New(v8Isolate, arrayPopCallback), v8::DontEnum); | |
| 1497 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "concat"), v8::Fu nctionTemplate::New(v8Isolate, concatCallback), v8::DontEnum); | |
| 1498 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "reverse"), v8::F unctionTemplate::New(v8Isolate, arrayReverseCallback), v8::DontEnum); | |
| 1499 | |
| 1500 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "shift"), v8::Fun ctionTemplate::New(v8Isolate, arrayShiftCallback), v8::DontEnum); | |
| 1501 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "unshift"), v8::F unctionTemplate::New(v8Isolate, arrayUnshiftCallback), v8::DontEnum); | |
| 1502 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "splice"), v8::Fu nctionTemplate::New(v8Isolate, arraySpliceCallback), v8::DontEnum); | |
| 1503 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "sort"), v8::Func tionTemplate::New(v8Isolate, arraySortCallback), v8::DontEnum); | |
| 1504 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "$toJsArray"), v8 ::FunctionTemplate::New(v8Isolate, toJsArrayCallback), v8::DontEnum); | |
| 1505 | |
| 1506 // ES6 experimental properties not currently supported that we could sup port if needed. | |
| 1507 // These would require building separate live proxy objects. | |
| 1508 // "entries", | |
| 1509 // "values", | |
| 1510 // "keys" | |
| 1511 | |
| 1512 v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(p roxyTemplateLocal); | |
| 1513 instanceTemplate->SetIndexedPropertyHandler(&indexedGetterArray, &indexe dSetterArray, 0, 0, &indexedEnumeratorArray); | |
| 1514 instanceTemplate->SetNamedPropertyHandler(&arrayNamedPropertyGetter, &ar rayNamedPropertySetter, &arrayQueryProperty, &arrayDeleteProperty, 0); | |
| 1515 } else { | |
| 1516 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate); | |
| 1517 } | |
| 1518 return proxyTemplateLocal; | |
| 1519 } | |
| 1520 | |
| 1521 } | |
| OLD | NEW |