OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * |
| 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are |
| 6 * met: |
| 7 * |
| 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above |
| 11 * copyright notice, this list of conditions and the following disclaimer |
| 12 * in the documentation and/or other materials provided with the |
| 13 * distribution. |
| 14 * * Neither the name of Google Inc. nor the names of its |
| 15 * contributors may be used to endorse or promote products derived from |
| 16 * this software without specific prior written permission. |
| 17 * |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ |
| 30 #include "config.h" |
| 31 |
| 32 #include "bindings/core/dart/DartJsInterop.h" |
| 33 |
| 34 #include "bindings/core/dart/DartDOMWrapper.h" |
| 35 #include "bindings/core/dart/DartHandleProxy.h" |
| 36 #include "bindings/core/dart/DartJsInteropData.h" |
| 37 #include "bindings/core/dart/DartPersistentValue.h" |
| 38 #include "bindings/core/dart/DartUtilities.h" |
| 39 #include "bindings/core/dart/V8Converter.h" |
| 40 #include "bindings/core/v8/V8Binding.h" |
| 41 #include "bindings/core/v8/V8Element.h" |
| 42 #include "bindings/core/v8/V8RecursionScope.h" |
| 43 #include "bindings/core/v8/V8ScriptRunner.h" |
| 44 #include "core/dom/Element.h" |
| 45 |
| 46 #include "wtf/StdLibExtras.h" |
| 47 |
| 48 #include <dart_api.h> |
| 49 #include <limits> |
| 50 |
| 51 namespace blink { |
| 52 |
| 53 const int JsObject::dartClassId = _JsObjectClassId; |
| 54 const int JsFunction::dartClassId = _JsFunctionClassId; |
| 55 const int JsArray::dartClassId = _JsArrayClassId; |
| 56 |
| 57 // TODO(jacobr): v8::String::NewFromUtf8 usages should be cached for constant |
| 58 // strings to improve performance. |
| 59 /** |
| 60 * Polyfill script to make Dart List objects look like JavaScript arrays when |
| 61 * passed to JavaScript via JavaScript interop. Handling the edge cases |
| 62 * requires patching the JavaScript Array prototype for a couple methods |
| 63 * and implementing all of the JavaScript Array methods on the JavaScript |
| 64 * proxy object for the Dart List. Some of the JavaScript methods are |
| 65 * implemented in Dart and C++ code, some are implemented in this JavaScript |
| 66 * polyfill script. |
| 67 */ |
| 68 const char* dartArrayPolyfill = |
| 69 "(function() {" |
| 70 // If filter has already been defined on the DartList prototype, |
| 71 // another DOM isolate has beaten us to polyfilling. |
| 72 " if ($DartList.prototype.hasOwnProperty('filter')) return;" |
| 73 // This hack is required so that someDartList instanceof Array == true. |
| 74 " $DartList.prototype.__proto__ = Array.prototype;" |
| 75 " $DartList.__proto__ = Array;" |
| 76 // This hack is required for libraries such as jQuery that detect arrays |
| 77 // by looking at the constructor property treat $DartList as Array. |
| 78 " $DartList.prototype.constructor = Array;" |
| 79 " var isArray = Array.isArray;" |
| 80 " var concat = Array.prototype.concat;" |
| 81 " var blob = Blob;" |
| 82 " function makeSafeArg(arg) {" |
| 83 " return (arg instanceof $DartList) ? arg.$toJsArray() : arg;" |
| 84 " };" |
| 85 " function makeSafeArgs(args) {" |
| 86 " var len = args.length;" |
| 87 " for (var i = 0; i < len; ++i) {" |
| 88 " var arg = args[i];" |
| 89 " if (arg instanceof $DartList) {" |
| 90 " args[i] = arg.$toJsArray();" |
| 91 " }" |
| 92 " }" |
| 93 " };" |
| 94 |
| 95 // If performance becomes an issue, we could implement these |
| 96 // methods in Dart instead of creating a shallow copy JavaScript |
| 97 // array containing the elements of the Dart List and calling |
| 98 // the JavaScript method. |
| 99 |
| 100 // Handle methods that take a callback with value, index, and thisArg |
| 101 // parameters. The trick is we need to make thisArg reference the |
| 102 // underlying Dart List rather than the JavaScript copy we create for |
| 103 // implementation convenience. |
| 104 " ['filter', 'forEach', 'some', 'every', 'map'].forEach(function(name) {" |
| 105 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va
lue: function(callback, thisArg) {" |
| 106 " var dartList = this;" |
| 107 " return this.$toJsArray()[name](function(value, index) {" |
| 108 " return callback.call(thisArg, value, index, dartList);" |
| 109 " });" |
| 110 " }});" |
| 111 " });" |
| 112 |
| 113 " ['slice', 'indexOf', 'lastIndexOf'].forEach(function(name) {" |
| 114 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va
lue: function() {" |
| 115 " var jsArray = this.$toJsArray();" |
| 116 " return jsArray[name].apply(jsArray, arguments);" |
| 117 " }});" |
| 118 " });" |
| 119 |
| 120 " ['reduce', 'reduceRight'].forEach(function(name) {" |
| 121 " Object.defineProperty($DartList.prototype, name, {enumerable: false, va
lue: function(callback, thisArg) {" |
| 122 " var dartList = this;" |
| 123 " return this.$toJsArray()[name](function(previousValue, currentValue,
index) {" |
| 124 " return callback.call(thisArg, previousValue, currentValue, index,
dartList);" |
| 125 " });" |
| 126 " }});" |
| 127 " });" |
| 128 |
| 129 // Arguments to concat that are Arrays are treated differently. |
| 130 // Warning: this will slow down general JavaScript array concat performance
in Dartium. |
| 131 " Array.prototype.concat = function() {" |
| 132 " makeSafeArgs(arguments);" |
| 133 " return concat.apply(this, arguments);" |
| 134 " };" |
| 135 |
| 136 // The Blob constructor cannot handle our psuedo Arrays. |
| 137 // Warning: this will slow down general Blob constructor performance in Dart
ium. |
| 138 " Blob = function(array, options) {" |
| 139 " if (arguments.length < 2) return new blob(makeSafeArg(array));" |
| 140 " else return new blob(makeSafeArg(array), options);" |
| 141 " };" |
| 142 |
| 143 // Need to make sure that Array.isArray returns true for Dart lists. |
| 144 " Array.isArray = function(arr) {" |
| 145 " return isArray(arr) || (arr instanceof $DartList);" |
| 146 " };" |
| 147 "})();"; |
| 148 |
| 149 static v8::Local<v8::FunctionTemplate> dartFunctionTemplate(); |
| 150 static v8::Local<v8::FunctionTemplate> dartObjectTemplate(); |
| 151 static v8::Local<v8::FunctionTemplate> dartListTemplate(); |
| 152 |
| 153 // TODO(jacobr): we should really be using this method everywhere instead of |
| 154 // sticking interop methods in the dart:html _Utils class. |
| 155 static Dart_Handle invokeTopLevelJsInteropMethod(DartDOMData* domData, const cha
r* methodName, int argCount, Dart_Handle* args) |
| 156 { |
| 157 Dart_PersistentHandle library = domData->jsLibrary(); |
| 158 ASSERT(!Dart_IsError(library)); |
| 159 return Dart_Invoke(library, Dart_NewStringFromCString(methodName), argCount,
args); |
| 160 } |
| 161 |
| 162 template<typename CallbackInfo> |
| 163 void setJsReturnValue(DartDOMData* domData, CallbackInfo info, Dart_Handle resul
t) |
| 164 { |
| 165 if (Dart_IsError(result)) { |
| 166 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 167 V8ThrowException::throwException(v8::String::NewFromUtf8(v8Isolate, Dart
_GetError(result)), v8Isolate); |
| 168 } else { |
| 169 Dart_Handle exception = 0; |
| 170 v8::Local<v8::Value> ret = JsInterop::fromDart(domData, result, exceptio
n); |
| 171 if (exception) { |
| 172 V8ThrowException::throwException(V8Converter::stringToV8(Dart_ToStri
ng(exception)), v8::Isolate::GetCurrent()); |
| 173 return; |
| 174 } |
| 175 v8SetReturnValue(info, ret); |
| 176 } |
| 177 } |
| 178 |
| 179 static void functionInvocationCallback(const v8::FunctionCallbackInfo<v8::Value>
& args) |
| 180 { |
| 181 DartScopes scopes(args.Holder()); |
| 182 Dart_Handle handle = scopes.handle; |
| 183 DartDOMData* domData = DartDOMData::current(); |
| 184 ASSERT(domData); |
| 185 ASSERT(DartUtilities::isFunction(domData, handle)); |
| 186 |
| 187 Vector<Dart_Handle> dartFunctionArgs; |
| 188 ASSERT(args.Length() == 1 || args.Length() == 2); |
| 189 // If there is 1 argument, we assume it is a v8:Array or arguments, if |
| 190 // there are 2 arguments, the first argument is "this" and the second |
| 191 // argument is an array of arguments. |
| 192 if (args.Length() > 1) { |
| 193 dartFunctionArgs.append(JsInterop::toDart(args[0])); |
| 194 } |
| 195 |
| 196 v8::Local<v8::Array> argsList = args[args.Length()-1].As<v8::Array>(); |
| 197 uint32_t argsListLength = argsList->Length(); |
| 198 for (uint32_t i = 0; i < argsListLength; i++) { |
| 199 dartFunctionArgs.append(JsInterop::toDart(argsList->Get(i))); |
| 200 } |
| 201 |
| 202 setJsReturnValue(domData, args, Dart_InvokeClosure(handle, dartFunctionArgs.
size(), dartFunctionArgs.data())); |
| 203 } |
| 204 |
| 205 static v8::Local<v8::ObjectTemplate> setupInstanceTemplate(v8::Local<v8::Functio
nTemplate> proxyTemplate) |
| 206 { |
| 207 v8::Local<v8::ObjectTemplate> instanceTemplate = proxyTemplate->InstanceTemp
late(); |
| 208 instanceTemplate->SetInternalFieldCount(1); |
| 209 return instanceTemplate; |
| 210 } |
| 211 |
| 212 static v8::Local<v8::FunctionTemplate> dartFunctionTemplate() |
| 213 { |
| 214 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ())
; |
| 215 v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| 216 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 217 if (proxyTemplate.IsEmpty()) { |
| 218 proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New
(v8Isolate)); |
| 219 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro
xyTemplate); |
| 220 v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(p
roxyTemplateLocal); |
| 221 |
| 222 instanceTemplate->SetCallAsFunctionHandler(&functionInvocationCallback); |
| 223 } else { |
| 224 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro
xyTemplate); |
| 225 } |
| 226 return proxyTemplateLocal; |
| 227 } |
| 228 |
| 229 static v8::Local<v8::FunctionTemplate> dartObjectTemplate() |
| 230 { |
| 231 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ())
; |
| 232 v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| 233 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 234 if (proxyTemplate.IsEmpty()) { |
| 235 proxyTemplate.Reset(v8Isolate, v8::FunctionTemplate::New(v8Isolate)); |
| 236 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro
xyTemplate); |
| 237 proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Dar
tObject")); |
| 238 setupInstanceTemplate(proxyTemplateLocal); |
| 239 } else { |
| 240 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro
xyTemplate); |
| 241 } |
| 242 return proxyTemplateLocal; |
| 243 } |
| 244 |
| 245 /** |
| 246 * Helper class to manage scopes needed for JSInterop code. |
| 247 */ |
| 248 class JsInteropScopes { |
| 249 public: |
| 250 Dart_NativeArguments args; |
| 251 v8::Context::Scope v8Scope; |
| 252 v8::TryCatch tryCatch; |
| 253 |
| 254 JsInteropScopes(Dart_NativeArguments args) |
| 255 : args(args) |
| 256 , v8Scope(DartUtilities::currentV8Context()) |
| 257 { |
| 258 ASSERT(v8::Isolate::GetCurrent()); |
| 259 } |
| 260 |
| 261 ~JsInteropScopes() |
| 262 { |
| 263 // The user is expected to call handleJsException before the scope is |
| 264 // closed so that V8 exceptions are properly sent back to Dart. |
| 265 ASSERT(!tryCatch.HasCaught()); |
| 266 } |
| 267 |
| 268 bool handleJsException(Dart_Handle* exception) |
| 269 { |
| 270 if (!tryCatch.HasCaught()) |
| 271 return false; |
| 272 // FIXME: terminate v8 if tryCatch.CanContinue() is false. |
| 273 ASSERT(tryCatch.CanContinue()); |
| 274 ASSERT(exception); |
| 275 v8::Handle<v8::Value> ex(tryCatch.Exception()->ToString()); |
| 276 if (ex.IsEmpty()) { |
| 277 *exception = Dart_NewStringFromCString("Empty JavaScript exception")
; |
| 278 } else { |
| 279 *exception = V8Converter::stringToDart(ex); |
| 280 } |
| 281 tryCatch.Reset(); |
| 282 return true; |
| 283 } |
| 284 |
| 285 void setReturnValue(Dart_Handle ret) |
| 286 { |
| 287 ASSERT(!tryCatch.HasCaught()); |
| 288 Dart_SetReturnValue(args, ret); |
| 289 } |
| 290 |
| 291 void setReturnValue(v8::Local<v8::Value> ret) |
| 292 { |
| 293 ASSERT(!tryCatch.HasCaught()); |
| 294 Dart_SetReturnValue(args, JsInterop::toDart(ret)); |
| 295 ASSERT(!tryCatch.HasCaught()); |
| 296 } |
| 297 |
| 298 void setReturnValueInteger(int64_t ret) |
| 299 { |
| 300 ASSERT(!tryCatch.HasCaught()); |
| 301 Dart_SetIntegerReturnValue(args, ret); |
| 302 } |
| 303 }; |
| 304 |
| 305 PassRefPtr<JsObject> JsObject::create(v8::Local<v8::Object> v8Handle) |
| 306 { |
| 307 return adoptRef(new JsObject(v8Handle)); |
| 308 } |
| 309 |
| 310 v8::Local<v8::Value> JsInterop::fromDart(DartDOMData* domData, Dart_Handle handl
e, Dart_Handle& exception) |
| 311 { |
| 312 v8::Handle<v8::Value> value = V8Converter::toV8IfPrimitive(domData, handle,
exception); |
| 313 if (!value.IsEmpty() || exception) |
| 314 return value; |
| 315 // TODO(terry): START of uncommented block by Jacob, I've re-enabled for cla
mped arrays... |
| 316 value = V8Converter::toV8IfBrowserNative(domData, handle, exception); |
| 317 if (!value.IsEmpty() || exception) |
| 318 return value; |
| 319 // TODO(terry): END of uncommented block by Jacob. |
| 320 if (DartDOMWrapper::subtypeOf(handle, JsObject::dartClassId)) { |
| 321 JsObject* object = DartDOMWrapper::unwrapDartWrapper<JsObject>(domData,
handle, exception); |
| 322 if (exception) |
| 323 return v8::Local<v8::Value>(); |
| 324 return object->localV8Object(); |
| 325 } |
| 326 |
| 327 if (DartUtilities::isFunction(domData, handle)) { |
| 328 v8::Local<v8::Object> functionProxy = dartFunctionTemplate()->InstanceTe
mplate()->NewInstance(); |
| 329 DartHandleProxy::writePointerToProxy(functionProxy, handle); |
| 330 // The raw functionProxy doesn't behave enough like a true JS function |
| 331 // so we wrap it in a true JS function. |
| 332 return domData->jsInteropData()->wrapDartFunction()->Call(functionProxy,
0, 0); |
| 333 } |
| 334 |
| 335 v8::Local<v8::Object> proxy; |
| 336 ASSERT(Dart_IsInstance(handle)); |
| 337 // Simulate the behavior of the Dart dev compiler where new List() is |
| 338 // equivalent to a JavaScript array. We accomplish this by creating a |
| 339 // JavaScript object that fakes that it is a JavaScript array but is |
| 340 // actually backed by a Dart list. This is not a breaking change as |
| 341 // existing Dart-JS interop passed arrays as opaque Dart handles. |
| 342 // The jsify method can still be called if you wish to create a copy |
| 343 // of a json like Dart data structure. |
| 344 if (Dart_IsList(handle)) { |
| 345 proxy = dartListTemplate()->InstanceTemplate()->NewInstance(); |
| 346 } else { |
| 347 proxy = dartObjectTemplate()->InstanceTemplate()->NewInstance(); |
| 348 } |
| 349 DartHandleProxy::writePointerToProxy(proxy, handle); |
| 350 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 351 proxy->SetHiddenValue(v8::String::NewFromUtf8(v8Isolate, "dartProxy"), v8::B
oolean::New(v8Isolate, true)); |
| 352 |
| 353 return proxy; |
| 354 } |
| 355 |
| 356 // Given a Blink Element instance, make it a V8Object that can then be returned |
| 357 // as a Dart JsObject that holds the Javascript object (V8Object). |
| 358 void JsInterop::returnBlinkElement(Element* value, Dart_NativeArguments args, Da
rt_Handle& exception) |
| 359 { |
| 360 JsInteropScopes scopes(args); |
| 361 |
| 362 V8ScriptState* state = DartUtilities::v8ScriptStateForCurrentIsolate(); |
| 363 |
| 364 // This is calling blink::Element::toV8(Element*,...) to return a V8Object f
rom |
| 365 // the Blink Element. |
| 366 v8::Handle<v8::Value> v8Value = toV8(value, state->context()->Global(), stat
e->isolate()); |
| 367 v8::Local<v8::Object> v8Handle = v8Value.As<v8::Object>(); |
| 368 |
| 369 // Return a Dart JsObject that wraps Element * back to the Dart return value
. |
| 370 scopes.setReturnValue(v8Handle); |
| 371 } |
| 372 |
| 373 // Given a Blink Element instance, make it a V8Object that can then be returned |
| 374 // as a Dart JsObject that holds the Javascript object (V8Object). |
| 375 v8::Local<v8::Object> JsInterop::blinkElement(Element* value, Dart_Handle& excep
tion) |
| 376 { |
| 377 V8ScriptState* state = DartUtilities::v8ScriptStateForCurrentIsolate(); |
| 378 |
| 379 // This is calling blink::Element::toV8(Element*,...) to return a V8Object f
rom |
| 380 // the Blink Element. |
| 381 v8::Handle<v8::Value> v8Value = toV8(value, state->context()->Global(), stat
e->isolate()); |
| 382 v8::Local<v8::Object> v8Handle = v8Value.As<v8::Object>(); |
| 383 return v8Handle; |
| 384 } |
| 385 |
| 386 JsObject::JsObject(v8::Local<v8::Object> v8Handle) |
| 387 { |
| 388 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| 389 v8::Persistent<v8::Object> persistentHandle; |
| 390 v8Object.Reset(isolate, v8Handle); |
| 391 } |
| 392 |
| 393 v8::Local<v8::Object> JsObject::localV8Object() |
| 394 { |
| 395 return v8::Local<v8::Object>::New(v8::Isolate::GetCurrent(), v8Object); |
| 396 } |
| 397 |
| 398 Dart_Handle JsInterop::toDart(v8::Local<v8::Value> v8Handle) |
| 399 { |
| 400 Dart_Handle handle = V8Converter::toDartIfPrimitive(v8Handle); |
| 401 if (handle) |
| 402 return handle; |
| 403 |
| 404 ASSERT(v8Handle->IsObject()); |
| 405 v8::Handle<v8::Object> object = v8Handle.As<v8::Object>(); |
| 406 // TODO(terry): START of uncommented block by Jacob, I've re-enabled for cla
mped arrays... |
| 407 Dart_Handle exception = 0; |
| 408 handle = V8Converter::toDartIfBrowserNative(object, object->CreationContext(
)->GetIsolate(), exception); |
| 409 ASSERT(!exception); |
| 410 if (handle) |
| 411 return handle; |
| 412 // TODO(terry): END of uncommented block by Jacob. |
| 413 // Unwrap objects passed from Dart to JS that are being passed back to |
| 414 // Dart. FIXME: we do not yet handle unwrapping JS functions passed |
| 415 // from Dart to JS as we have to wrap them with true JS Function objects. |
| 416 // If this use case is important we can support it at the cost of hanging |
| 417 // an extra expando off the JS function wrapping the Dart function. |
| 418 if (DartHandleProxy::isDartProxy(v8Handle)) { |
| 419 DartPersistentValue* scriptValue = DartHandleProxy::readPointerFromProxy
(v8Handle); |
| 420 ASSERT(scriptValue->isIsolateAlive()); |
| 421 // If the isolate does not match we fall back to using the existing JS |
| 422 // wrapper for the Dart object so that simple cases that would work in |
| 423 // Dart2Js work. We could alternately throw an exception here. |
| 424 if (scriptValue->isolate() == Dart_CurrentIsolate()) { |
| 425 return scriptValue->value(); |
| 426 } |
| 427 } |
| 428 |
| 429 return JsObject::toDart(object); |
| 430 } |
| 431 |
| 432 Dart_Handle JsObject::toDart(v8::Local<v8::Object> object) |
| 433 { |
| 434 // FIXME: perform caching so that === can be used. |
| 435 if (object->IsFunction()) { |
| 436 RefPtr<JsFunction> jsFunction = JsFunction::create(object.As<v8::Functio
n>()); |
| 437 return JsFunction::toDart(jsFunction); |
| 438 } |
| 439 |
| 440 if (object->IsArray()) { |
| 441 RefPtr<JsArray> jsArray = JsArray::create(object.As<v8::Array>()); |
| 442 return JsArray::toDart(jsArray); |
| 443 } |
| 444 |
| 445 RefPtr<JsObject> jsObject = JsObject::create(object); |
| 446 return JsObject::toDart(jsObject); |
| 447 } |
| 448 |
| 449 static void maybeCreateJsObjectImplClass(DartDOMData* domData) |
| 450 { |
| 451 DartJsInteropData* interopData = domData->jsInteropData(); |
| 452 // Skip if the JSObjectImpl class has already been defined. |
| 453 if (interopData->jsObjectImplDefined()) { |
| 454 return; |
| 455 } |
| 456 // Helper method that generates boilerplate source code for |
| 457 // JsObjectImpl, JsFunctionImpl, and JsArrayImpl classes that implement |
| 458 // all Dart types that have been passed to the dart:js registerJsInterfaces |
| 459 // method. The sole purpose of these classes is to ensure that checked mode |
| 460 // allows casting a JsObject to all types implemented by a JsObject. |
| 461 Dart_Handle source = invokeTopLevelJsInteropMethod(domData, "_generateJsObje
ctImplPart", 0, 0); |
| 462 ASSERT(Dart_IsString(source)); |
| 463 |
| 464 Dart_Handle ALLOW_UNUSED ret = Dart_LibraryLoadPatch(domData->jsLibrary(), D
art_NewStringFromCString("JsInteropImpl.dart"), source); |
| 465 ASSERT(!Dart_IsError(ret)); |
| 466 ret = Dart_FinalizeLoading(false); |
| 467 ASSERT(!Dart_IsError(ret)); |
| 468 |
| 469 interopData->setJsObjectImplDefined(); |
| 470 |
| 471 // Start of polyfill work to make Dart List proxies behave like JavaScript |
| 472 // Arrays by monkey patching JavaScript Array and the List JavaScript |
| 473 // proxies as needed. |
| 474 v8::Context::Scope scope(DartUtilities::currentV8Context()); |
| 475 |
| 476 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 477 |
| 478 v8::Local<v8::Function> dartArrayConstructor = dartListTemplate()->GetFuncti
on(); |
| 479 |
| 480 DartUtilities::currentV8Context()->Global()->Set(v8::String::NewFromUtf8(v8I
solate, "$DartList"), |
| 481 dartArrayConstructor); |
| 482 V8ScriptRunner::compileAndRunInternalScript(v8::String::NewFromUtf8(v8Isolat
e, dartArrayPolyfill), v8Isolate); |
| 483 } |
| 484 |
| 485 Dart_Handle JsObject::toDart(PassRefPtr<JsObject> jsObject) |
| 486 { |
| 487 DartDOMData* domData = DartDOMData::current(); |
| 488 // We need to ensure JsObjectImpl exists before creating the wrapper. |
| 489 maybeCreateJsObjectImplClass(domData); |
| 490 return DartDOMWrapper::createWrapper<JsObject>(domData, jsObject.get(), JsOb
ject::dartClassId); |
| 491 } |
| 492 |
| 493 JsObject::~JsObject() |
| 494 { |
| 495 v8Object.Reset(); |
| 496 } |
| 497 |
| 498 Dart_Handle JsFunction::toDart(PassRefPtr<JsFunction> jsFunction) |
| 499 { |
| 500 DartDOMData* domData = DartDOMData::current(); |
| 501 // We need to ensure JsObjectImpl exists before creating the wrapper. |
| 502 maybeCreateJsObjectImplClass(domData); |
| 503 return DartDOMWrapper::createWrapper<JsFunction>(domData, jsFunction.get(),
JsFunction::dartClassId); |
| 504 } |
| 505 |
| 506 JsFunction::JsFunction(v8::Local<v8::Function> v8Handle) : JsObject(v8Handle) {
} |
| 507 |
| 508 PassRefPtr<JsFunction> JsFunction::create(v8::Local<v8::Function> v8Handle) |
| 509 { |
| 510 return adoptRef(new JsFunction(v8Handle)); |
| 511 } |
| 512 |
| 513 v8::Local<v8::Function> JsFunction::localV8Function() |
| 514 { |
| 515 return localV8Object().As<v8::Function>(); |
| 516 } |
| 517 |
| 518 Dart_Handle JsArray::toDart(PassRefPtr<JsArray> jsArray) |
| 519 { |
| 520 DartDOMData* domData = DartDOMData::current(); |
| 521 // We need to ensure JsArrayImpl exists before creating the wrapper. |
| 522 maybeCreateJsObjectImplClass(domData); |
| 523 return DartDOMWrapper::createWrapper<JsArray>(domData, jsArray.get(), JsArra
y::dartClassId); |
| 524 } |
| 525 |
| 526 JsArray::JsArray(v8::Local<v8::Array> v8Handle) : JsObject(v8Handle) { } |
| 527 |
| 528 PassRefPtr<JsArray> JsArray::create(v8::Local<v8::Array> v8Handle) |
| 529 { |
| 530 return adoptRef(new JsArray(v8Handle)); |
| 531 } |
| 532 |
| 533 v8::Local<v8::Array> JsArray::localV8Array() |
| 534 { |
| 535 return localV8Object().As<v8::Array>(); |
| 536 } |
| 537 |
| 538 namespace JsInteropInternal { |
| 539 |
| 540 typedef HashMap<Dart_Handle, v8::Handle<v8::Value> > DartHandleToV8Map; |
| 541 v8::Handle<v8::Value> jsifyHelper(DartDOMData*, Dart_Handle value, DartHandleToV
8Map&, Dart_Handle& exception); |
| 542 |
| 543 void argsListToV8(DartDOMData* domData, Dart_Handle args, Vector<v8::Local<v8::V
alue> >* v8Args, Dart_Handle& exception) |
| 544 { |
| 545 if (Dart_IsNull(args)) |
| 546 return; |
| 547 |
| 548 if (!Dart_IsList(args)) { |
| 549 exception = Dart_NewStringFromCString("args not type list"); |
| 550 return; |
| 551 } |
| 552 |
| 553 intptr_t argsLength = 0; |
| 554 Dart_ListLength(args, &argsLength); |
| 555 for (intptr_t i = 0; i < argsLength; i++) { |
| 556 v8Args->append(JsInterop::fromDart(domData, Dart_ListGetAt(args, i), exc
eption)); |
| 557 if (exception) |
| 558 return; |
| 559 } |
| 560 } |
| 561 |
| 562 void argsListToV8DebuggerOnly(DartDOMData* domData, Dart_Handle args, Vector<v8:
:Local<v8::Value> >* v8Args, Dart_Handle& exception) |
| 563 { |
| 564 if (Dart_IsNull(args)) |
| 565 return; |
| 566 |
| 567 if (!Dart_IsList(args)) { |
| 568 exception = Dart_NewStringFromCString("args not type list"); |
| 569 return; |
| 570 } |
| 571 |
| 572 intptr_t argsLength = 0; |
| 573 Dart_ListLength(args, &argsLength); |
| 574 for (intptr_t i = 0; i < argsLength; i++) { |
| 575 v8Args->append(DartHandleProxy::create(Dart_ListGetAt(args, i))); |
| 576 } |
| 577 } |
| 578 |
| 579 static void jsObjectConstructorCallback(Dart_NativeArguments args) |
| 580 { |
| 581 Dart_Handle exception = 0; |
| 582 { |
| 583 JsInteropScopes scopes(args); |
| 584 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 585 v8::Local<v8::Value> constructorArg = JsInterop::fromDart(domData, Dart_
GetNativeArgument(args, 0), exception); |
| 586 if (exception) |
| 587 goto fail; |
| 588 |
| 589 if (!constructorArg->IsFunction()) { |
| 590 exception = Dart_NewStringFromCString("constructor not a function"); |
| 591 goto fail; |
| 592 } |
| 593 |
| 594 Vector<v8::Local<v8::Value> > v8Args; |
| 595 argsListToV8(domData, Dart_GetNativeArgument(args, 1), &v8Args, exceptio
n); |
| 596 |
| 597 v8::Local<v8::Value> ret = constructorArg.As<v8::Function>()->CallAsCons
tructor(v8Args.size(), v8Args.data()); |
| 598 crashIfV8IsDead(); |
| 599 |
| 600 if (scopes.handleJsException(&exception)) |
| 601 goto fail; |
| 602 |
| 603 // Intentionally skip auto-conversion in this case as the user expects |
| 604 // a JSObject. FIXME: evaluate if this is the right solution. |
| 605 // Alternately, we could throw an exception. |
| 606 if (ret->IsObject()) { |
| 607 scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>())); |
| 608 } else { |
| 609 // This will throw an exception in Dart checked mode. |
| 610 scopes.setReturnValue(ret); |
| 611 } |
| 612 return; |
| 613 } |
| 614 |
| 615 fail: |
| 616 Dart_ThrowException(exception); |
| 617 ASSERT_NOT_REACHED(); |
| 618 } |
| 619 |
| 620 static void identityEqualityCallback(Dart_NativeArguments args) |
| 621 { |
| 622 Dart_Handle exception = 0; |
| 623 { |
| 624 JsInteropScopes scopes(args); |
| 625 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 626 v8::Local<v8::Value> a = JsInterop::fromDart(domData, Dart_GetNativeArgu
ment(args, 0), exception); |
| 627 if (exception) |
| 628 goto fail; |
| 629 v8::Local<v8::Value> b = JsInterop::fromDart(domData, Dart_GetNativeArgu
ment(args, 1), exception); |
| 630 if (exception) |
| 631 goto fail; |
| 632 |
| 633 bool strictEquals = a->StrictEquals(b); |
| 634 |
| 635 if (scopes.handleJsException(&exception)) |
| 636 goto fail; |
| 637 scopes.setReturnValue(DartUtilities::boolToDart(strictEquals)); |
| 638 return; |
| 639 } |
| 640 |
| 641 fail: |
| 642 Dart_ThrowException(exception); |
| 643 ASSERT_NOT_REACHED(); |
| 644 } |
| 645 |
| 646 static void getterCallback(Dart_NativeArguments args) |
| 647 { |
| 648 Dart_Handle exception = 0; |
| 649 { |
| 650 JsInteropScopes scopes(args); |
| 651 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 652 v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
| 653 Dart_Handle index = Dart_GetNativeArgument(args, 1); |
| 654 uint64_t intIndex = 0; |
| 655 v8::Local<v8::Value> ret; |
| 656 |
| 657 if (Dart_IsInteger(index)) { |
| 658 bool isUint64 = false; |
| 659 Dart_IntegerFitsIntoUint64(index, &isUint64); |
| 660 if (isUint64) { |
| 661 Dart_Handle ALLOW_UNUSED result = Dart_IntegerToUint64(index, &i
ntIndex); |
| 662 if (intIndex <= std::numeric_limits<uint32_t>::max()) { |
| 663 ASSERT(!Dart_IsError(result)); |
| 664 ret = v8Receiver->Get((uint32_t)intIndex); |
| 665 } else { |
| 666 ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
| 667 } |
| 668 } else { |
| 669 ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
| 670 } |
| 671 } else if (Dart_IsString(index)) { |
| 672 ret = v8Receiver->Get(V8Converter::stringToV8(index)); |
| 673 } else if (Dart_IsNumber(index)) { |
| 674 ret = v8Receiver->Get(V8Converter::numberToV8(index)); |
| 675 } else { |
| 676 ret = v8Receiver->Get(V8Converter::stringToV8(Dart_ToString(index)))
; |
| 677 } |
| 678 |
| 679 if (scopes.handleJsException(&exception)) |
| 680 goto fail; |
| 681 scopes.setReturnValue(ret); |
| 682 return; |
| 683 } |
| 684 |
| 685 fail: |
| 686 Dart_ThrowException(exception); |
| 687 ASSERT_NOT_REACHED(); |
| 688 } |
| 689 |
| 690 static void hasPropertyCallback(Dart_NativeArguments args) |
| 691 { |
| 692 Dart_Handle exception = 0; |
| 693 { |
| 694 JsInteropScopes scopes(args); |
| 695 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 696 v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
| 697 Dart_Handle property = Dart_GetNativeArgument(args, 1); |
| 698 |
| 699 if (!Dart_IsString(property)) |
| 700 property = Dart_ToString(property); |
| 701 |
| 702 bool hasProperty = v8Receiver->Has(V8Converter::stringToV8(property)); |
| 703 if (scopes.handleJsException(&exception)) |
| 704 goto fail; |
| 705 scopes.setReturnValue(DartUtilities::boolToDart(hasProperty)); |
| 706 return; |
| 707 } |
| 708 |
| 709 fail: |
| 710 Dart_ThrowException(exception); |
| 711 ASSERT_NOT_REACHED(); |
| 712 } |
| 713 |
| 714 static void deletePropertyCallback(Dart_NativeArguments args) |
| 715 { |
| 716 Dart_Handle exception = 0; |
| 717 { |
| 718 JsInteropScopes scopes(args); |
| 719 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 720 v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
| 721 Dart_Handle property = Dart_GetNativeArgument(args, 1); |
| 722 if (!Dart_IsString(property)) |
| 723 property = Dart_ToString(property); |
| 724 |
| 725 v8Receiver->Delete(V8Converter::stringToV8(property)); |
| 726 if (scopes.handleJsException(&exception)) |
| 727 goto fail; |
| 728 return; |
| 729 } |
| 730 |
| 731 fail: |
| 732 Dart_ThrowException(exception); |
| 733 ASSERT_NOT_REACHED(); |
| 734 } |
| 735 |
| 736 static void instanceofCallback(Dart_NativeArguments args) |
| 737 { |
| 738 Dart_Handle exception = 0; |
| 739 { |
| 740 JsInteropScopes scopes(args); |
| 741 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 742 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 743 v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
| 744 v8::Local<v8::Value> type = JsInterop::fromDart(domData, Dart_GetNativeA
rgument(args, 1), exception); |
| 745 |
| 746 // FIXME: we could optimize the following lines slightly as the return |
| 747 // type is bool. |
| 748 v8::Local<v8::Value> ret = domData->jsInteropData()->instanceofFunction(
)->Call(v8Receiver, 1, &type); |
| 749 if (scopes.handleJsException(&exception)) |
| 750 goto fail; |
| 751 scopes.setReturnValue(ret); |
| 752 return; |
| 753 } |
| 754 |
| 755 fail: |
| 756 Dart_ThrowException(exception); |
| 757 ASSERT_NOT_REACHED(); |
| 758 } |
| 759 |
| 760 static void setterCallback(Dart_NativeArguments args) |
| 761 { |
| 762 Dart_Handle exception = 0; |
| 763 { |
| 764 JsInteropScopes scopes(args); |
| 765 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 766 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 767 v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
| 768 Dart_Handle index = Dart_GetNativeArgument(args, 1); |
| 769 v8::Local<v8::Value> value = JsInterop::fromDart(domData, Dart_GetNative
Argument(args, 2), exception); |
| 770 if (exception) |
| 771 goto fail; |
| 772 uint64_t intIndex = 0; |
| 773 bool ret = false; |
| 774 if (Dart_IsInteger(index)) { |
| 775 bool isUint64 = false; |
| 776 Dart_IntegerFitsIntoUint64(index, &isUint64); |
| 777 if (isUint64) { |
| 778 Dart_Handle ALLOW_UNUSED result = Dart_IntegerToUint64(index, &i
ntIndex); |
| 779 if (intIndex <= std::numeric_limits<uint32_t>::max()) { |
| 780 ASSERT(!Dart_IsError(result)); |
| 781 ret = v8Receiver->Set((uint32_t)intIndex, value); |
| 782 } else { |
| 783 ret = v8Receiver->Set(V8Converter::numberToV8(index), value)
; |
| 784 } |
| 785 } else { |
| 786 ret = v8Receiver->Set(V8Converter::numberToV8(index), value); |
| 787 } |
| 788 } else if (Dart_IsString(index)) { |
| 789 ret = v8Receiver->Set(V8Converter::stringToV8(index), value); |
| 790 } else if (Dart_IsNumber(index)) { |
| 791 ret = v8Receiver->Set(V8Converter::numberToV8(index), value); |
| 792 } else { |
| 793 ret = v8Receiver->Set(V8Converter::stringToV8(Dart_ToString(index)),
value); |
| 794 } |
| 795 |
| 796 if (scopes.handleJsException(&exception)) |
| 797 goto fail; |
| 798 scopes.setReturnValue(DartUtilities::boolToDart(ret)); |
| 799 return; |
| 800 } |
| 801 |
| 802 fail: |
| 803 Dart_ThrowException(exception); |
| 804 ASSERT_NOT_REACHED(); |
| 805 } |
| 806 |
| 807 static void hashCodeCallback(Dart_NativeArguments args) |
| 808 { |
| 809 Dart_Handle exception = 0; |
| 810 { |
| 811 JsInteropScopes scopes(args); |
| 812 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 813 int hashCode = receiver->localV8Object()->GetIdentityHash(); |
| 814 // FIXME: salt the v8 hashcode so we don't leak information about v8 |
| 815 // memory allocation. |
| 816 if (scopes.handleJsException(&exception)) |
| 817 goto fail; |
| 818 scopes.setReturnValueInteger(hashCode); |
| 819 return; |
| 820 } |
| 821 |
| 822 fail: |
| 823 Dart_ThrowException(exception); |
| 824 ASSERT_NOT_REACHED(); |
| 825 } |
| 826 |
| 827 static void callMethodCallback(Dart_NativeArguments args) |
| 828 { |
| 829 Dart_Handle exception = 0; |
| 830 { |
| 831 JsInteropScopes scopes(args); |
| 832 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 833 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 834 v8::Local<v8::Object> v8Receiver = receiver->localV8Object(); |
| 835 |
| 836 Dart_Handle name = Dart_GetNativeArgument(args, 1); |
| 837 |
| 838 Vector<v8::Local<v8::Value> > v8Args; |
| 839 argsListToV8(domData, Dart_GetNativeArgument(args, 2), &v8Args, exceptio
n); |
| 840 if (exception) |
| 841 goto fail; |
| 842 if (!Dart_IsString(name)) |
| 843 name = Dart_ToString(name); |
| 844 |
| 845 v8::Local<v8::Value> value = v8Receiver->Get(V8Converter::stringToV8(nam
e)); |
| 846 v8::Local<v8::Value> ret; |
| 847 if (value->IsFunction()) { |
| 848 ret = V8ScriptRunner::callFunction(value.As<v8::Function>(), DartUti
lities::scriptExecutionContext(), receiver->localV8Object(), v8Args.size(), v8Ar
gs.data(), v8::Isolate::GetCurrent()); |
| 849 } else if (value->IsObject()) { |
| 850 ret = V8ScriptRunner::callAsFunction(v8::Isolate::GetCurrent(), valu
e.As<v8::Object>(), receiver->localV8Object(), v8Args.size(), v8Args.data()); |
| 851 } else { |
| 852 // FIXME: we currently convert this exception to a NoSuchMethod |
| 853 // exception in the Dart code that wraps this native method. |
| 854 // Consider throwing a NoSuchMethod exception directly instead. |
| 855 exception = Dart_NewStringFromCString("property is not a function"); |
| 856 goto fail; |
| 857 } |
| 858 |
| 859 if (scopes.handleJsException(&exception)) |
| 860 goto fail; |
| 861 scopes.setReturnValue(ret); |
| 862 return; |
| 863 } |
| 864 |
| 865 fail: |
| 866 Dart_ThrowException(exception); |
| 867 ASSERT_NOT_REACHED(); |
| 868 } |
| 869 |
| 870 static void newJsArrayCallback(Dart_NativeArguments args) |
| 871 { |
| 872 JsInteropScopes scopes(args); |
| 873 scopes.setReturnValue(JsObject::toDart(v8::Array::New(v8::Isolate::GetCurren
t()))); |
| 874 return; |
| 875 } |
| 876 |
| 877 static void newJsArrayFromSafeListCallback(Dart_NativeArguments args) |
| 878 { |
| 879 Dart_Handle exception = 0; |
| 880 { |
| 881 JsInteropScopes scopes(args); |
| 882 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 883 Dart_Handle list = Dart_GetNativeArgument(args, 0); |
| 884 // Code on the Dart side insures this arg is a native Dart list. |
| 885 ASSERT(Dart_IsList(list)); |
| 886 |
| 887 intptr_t length = 0; |
| 888 Dart_Handle result = Dart_ListLength(list, &length); |
| 889 ASSERT(!Dart_IsError(result)); |
| 890 v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), l
ength); |
| 891 |
| 892 for (intptr_t i = 0; i < length; ++i) { |
| 893 result = Dart_ListGetAt(list, i); |
| 894 ASSERT(!Dart_IsError(result)); |
| 895 v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result,
exception); |
| 896 if (exception) |
| 897 goto fail; |
| 898 |
| 899 array->Set(i, v8value); |
| 900 } |
| 901 scopes.setReturnValue(array); |
| 902 return; |
| 903 } |
| 904 |
| 905 fail: |
| 906 Dart_ThrowException(exception); |
| 907 ASSERT_NOT_REACHED(); |
| 908 } |
| 909 |
| 910 |
| 911 static void jsArrayLengthCallback(Dart_NativeArguments args) |
| 912 { |
| 913 Dart_Handle exception = 0; |
| 914 { |
| 915 JsInteropScopes scopes(args); |
| 916 JsArray* receiver = DartDOMWrapper::receiver<JsArray>(args); |
| 917 uint32_t length = receiver->localV8Array()->Length(); |
| 918 if (scopes.handleJsException(&exception)) |
| 919 goto fail; |
| 920 scopes.setReturnValueInteger(length); |
| 921 return; |
| 922 } |
| 923 |
| 924 fail: |
| 925 Dart_ThrowException(exception); |
| 926 ASSERT_NOT_REACHED(); |
| 927 } |
| 928 |
| 929 static void fromBrowserObjectCallback(Dart_NativeArguments args) |
| 930 { |
| 931 Dart_Handle exception = 0; |
| 932 { |
| 933 JsInteropScopes scopes(args); |
| 934 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 935 |
| 936 v8::Local<v8::Value> ret = V8Converter::toV8IfBrowserNative(domData, Dar
t_GetNativeArgument(args, 0), exception); |
| 937 if (ret.IsEmpty()) { |
| 938 exception = Dart_NewStringFromCString("object must be an Node, Array
Buffer, Blob, ImageData, or IDBKeyRange"); |
| 939 goto fail; |
| 940 } |
| 941 if (exception) |
| 942 goto fail; |
| 943 ASSERT(ret->IsObject()); |
| 944 if (scopes.handleJsException(&exception)) |
| 945 goto fail; |
| 946 scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>())); |
| 947 return; |
| 948 } |
| 949 |
| 950 fail: |
| 951 Dart_ThrowException(exception); |
| 952 ASSERT_NOT_REACHED(); |
| 953 } |
| 954 |
| 955 static void applyCallback(Dart_NativeArguments args) |
| 956 { |
| 957 Dart_Handle exception = 0; |
| 958 { |
| 959 JsInteropScopes scopes(args); |
| 960 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 961 JsFunction* receiver = DartDOMWrapper::receiver<JsFunction>(args); |
| 962 |
| 963 Vector<v8::Local<v8::Value> > v8Args; |
| 964 argsListToV8(domData, Dart_GetNativeArgument(args, 1), &v8Args, exceptio
n); |
| 965 if (exception) |
| 966 goto fail; |
| 967 |
| 968 v8::Local<v8::Value> thisArg; |
| 969 Dart_Handle thisArgDart = Dart_GetNativeArgument(args, 2); |
| 970 if (Dart_IsNull(thisArgDart)) { |
| 971 // Use the global v8 object if no Dart thisArg was passed in. |
| 972 thisArg = DartUtilities::currentV8Context()->Global(); |
| 973 } else { |
| 974 Dart_Handle jso = Dart_GetField(thisArgDart, Dart_NewStringFromCStri
ng("blink_jsObject")); |
| 975 if (!Dart_IsError(jso) && DartDOMWrapper::subtypeOf(jso, JsObject::d
artClassId)) { |
| 976 // Use the blink JS Interop object. |
| 977 JsObject* object = DartDOMWrapper::unwrapDartWrapper<JsObject>(d
omData, jso, exception); |
| 978 if (exception) |
| 979 thisArg = v8::Local<v8::Value>(); |
| 980 thisArg = object->localV8Object(); |
| 981 } else { |
| 982 thisArg = JsInterop::fromDart(domData, thisArgDart, exception); |
| 983 if (exception) |
| 984 goto fail; |
| 985 } |
| 986 if (!thisArg->IsObject()) { |
| 987 exception = Dart_NewStringFromCString("thisArg is not an object"
); |
| 988 goto fail; |
| 989 } |
| 990 } |
| 991 |
| 992 v8::Local<v8::Value> ret = V8ScriptRunner::callFunction(receiver->localV
8Function(), DartUtilities::scriptExecutionContext(), thisArg.As<v8::Object>(),
v8Args.size(), v8Args.data(), v8::Isolate::GetCurrent()); |
| 993 if (scopes.handleJsException(&exception)) |
| 994 goto fail; |
| 995 scopes.setReturnValue(ret); |
| 996 return; |
| 997 } |
| 998 |
| 999 fail: |
| 1000 Dart_ThrowException(exception); |
| 1001 ASSERT_NOT_REACHED(); |
| 1002 } |
| 1003 |
| 1004 static void applyDebuggerOnlyCallback(Dart_NativeArguments args) |
| 1005 { |
| 1006 Dart_Handle exception = 0; |
| 1007 { |
| 1008 JsInteropScopes scopes(args); |
| 1009 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 1010 JsFunction* receiver = DartDOMWrapper::receiver<JsFunction>(args); |
| 1011 |
| 1012 Vector<v8::Local<v8::Value> > v8Args; |
| 1013 argsListToV8DebuggerOnly(domData, Dart_GetNativeArgument(args, 1), &v8Ar
gs, exception); |
| 1014 if (exception) |
| 1015 goto fail; |
| 1016 |
| 1017 v8::Local<v8::Value> thisArg; |
| 1018 Dart_Handle thisArgDart = Dart_GetNativeArgument(args, 2); |
| 1019 if (Dart_IsNull(thisArgDart)) { |
| 1020 // Use the global v8 object if no Dart thisArg was passed in. |
| 1021 thisArg = DartUtilities::currentV8Context()->Global(); |
| 1022 } else { |
| 1023 thisArg = JsInterop::fromDart(domData, thisArgDart, exception); |
| 1024 if (exception) |
| 1025 goto fail; |
| 1026 if (!thisArg->IsObject()) { |
| 1027 exception = Dart_NewStringFromCString("thisArg is not an object"
); |
| 1028 goto fail; |
| 1029 } |
| 1030 } |
| 1031 |
| 1032 v8::Local<v8::Value> ret = V8ScriptRunner::callFunction(receiver->localV
8Function(), DartUtilities::scriptExecutionContext(), thisArg.As<v8::Object>(),
v8Args.size(), v8Args.data(), v8::Isolate::GetCurrent()); |
| 1033 if (scopes.handleJsException(&exception)) |
| 1034 goto fail; |
| 1035 scopes.setReturnValue(ret); |
| 1036 return; |
| 1037 } |
| 1038 |
| 1039 fail: |
| 1040 Dart_ThrowException(exception); |
| 1041 ASSERT_NOT_REACHED(); |
| 1042 } |
| 1043 |
| 1044 static void toStringCallback(Dart_NativeArguments args) |
| 1045 { |
| 1046 Dart_Handle exception = 0; |
| 1047 { |
| 1048 JsInteropScopes scopes(args); |
| 1049 JsObject* receiver = DartDOMWrapper::receiver<JsObject>(args); |
| 1050 v8::Local<v8::Object> v8Object = receiver->localV8Object(); |
| 1051 if (scopes.handleJsException(&exception)) |
| 1052 goto fail; |
| 1053 if (v8Object.IsEmpty()) { |
| 1054 exception = Dart_NewStringFromCString("Invalid v8 handle"); |
| 1055 goto fail; |
| 1056 } |
| 1057 |
| 1058 v8::Local<v8::String> v8String = v8Object->ToString(); |
| 1059 if (scopes.handleJsException(&exception)) |
| 1060 goto fail; |
| 1061 scopes.setReturnValue(v8String); |
| 1062 return; |
| 1063 } |
| 1064 fail: |
| 1065 Dart_ThrowException(exception); |
| 1066 ASSERT_NOT_REACHED(); |
| 1067 } |
| 1068 |
| 1069 static void contextCallback(Dart_NativeArguments args) |
| 1070 { |
| 1071 v8::Local<v8::Context> v8Context = DartUtilities::currentV8Context(); |
| 1072 v8::Context::Scope scope(v8Context); |
| 1073 Dart_SetReturnValue(args, JsObject::toDart(v8Context->Global())); |
| 1074 } |
| 1075 |
| 1076 static void finalizeJsInterfacesCallback(Dart_NativeArguments args) |
| 1077 { |
| 1078 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(a
rgs)); |
| 1079 maybeCreateJsObjectImplClass(domData); |
| 1080 } |
| 1081 |
| 1082 static void interfacesFinalizedCallback(Dart_NativeArguments args) |
| 1083 { |
| 1084 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateData(a
rgs)); |
| 1085 DartJsInteropData* interopData = domData->jsInteropData(); |
| 1086 // Skip if the JSObjectImpl class has already been defined. |
| 1087 Dart_SetBooleanReturnValue(args, interopData->jsObjectImplDefined()); |
| 1088 } |
| 1089 |
| 1090 v8::Handle<v8::Value> mapToV8(DartDOMData* domData, Dart_Handle value, DartHandl
eToV8Map& map, Dart_Handle& exception) |
| 1091 { |
| 1092 Dart_Handle asList = DartUtilities::invokeUtilsMethod("convertMapToList", 1,
&value); |
| 1093 if (!DartUtilities::checkResult(asList, exception)) |
| 1094 return v8::Handle<v8::Value>(); |
| 1095 ASSERT(Dart_IsList(asList)); |
| 1096 |
| 1097 // Now we have a list [key, value, key, value, ....], create a v8 object and
set necesary |
| 1098 // properties on it. |
| 1099 v8::Handle<v8::Object> object = v8::Object::New(v8::Isolate::GetCurrent()); |
| 1100 map.set(value, object); |
| 1101 |
| 1102 // We converted to internal Dart list, methods shouldn't throw exceptions no
w. |
| 1103 intptr_t length = 0; |
| 1104 Dart_Handle ALLOW_UNUSED result = Dart_ListLength(asList, &length); |
| 1105 ASSERT(!Dart_IsError(result)); |
| 1106 ASSERT(!(length % 2)); |
| 1107 for (intptr_t i = 0; i < length; i += 2) { |
| 1108 v8::Handle<v8::Value> key = jsifyHelper(domData, Dart_ListGetAt(asList,
i), map, exception); |
| 1109 if (exception) |
| 1110 return v8::Handle<v8::Value>(); |
| 1111 v8::Handle<v8::Value> value = jsifyHelper(domData, Dart_ListGetAt(asList
, i + 1), map, exception); |
| 1112 if (exception) |
| 1113 return v8::Handle<v8::Value>(); |
| 1114 |
| 1115 object->Set(key, value); |
| 1116 } |
| 1117 |
| 1118 return object; |
| 1119 } |
| 1120 |
| 1121 v8::Handle<v8::Value> listToV8(DartDOMData* domData, Dart_Handle value, DartHand
leToV8Map& map, Dart_Handle& exception) |
| 1122 { |
| 1123 ASSERT(Dart_IsList(value)); |
| 1124 |
| 1125 intptr_t length = 0; |
| 1126 Dart_Handle result = Dart_ListLength(value, &length); |
| 1127 if (!DartUtilities::checkResult(result, exception)) |
| 1128 return v8::Handle<v8::Value>(); |
| 1129 |
| 1130 v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), lengt
h); |
| 1131 map.set(value, array); |
| 1132 |
| 1133 for (intptr_t i = 0; i < length; ++i) { |
| 1134 result = Dart_ListGetAt(value, i); |
| 1135 if (!DartUtilities::checkResult(result, exception)) |
| 1136 return v8::Handle<v8::Value>(); |
| 1137 v8::Handle<v8::Value> v8value = jsifyHelper(domData, result, map, except
ion); |
| 1138 if (exception) |
| 1139 return v8::Handle<v8::Value>(); |
| 1140 array->Set(i, v8value); |
| 1141 } |
| 1142 |
| 1143 return array; |
| 1144 } |
| 1145 |
| 1146 v8::Handle<v8::Value> jsifyHelper(DartDOMData* domData, Dart_Handle value, DartH
andleToV8Map& map, Dart_Handle& exception) |
| 1147 { |
| 1148 DartHandleToV8Map::iterator iter = map.find(value); |
| 1149 if (iter != map.end()) |
| 1150 return iter->value; |
| 1151 |
| 1152 if (Dart_IsList(value)) |
| 1153 return listToV8(domData, value, map, exception); |
| 1154 |
| 1155 bool isMap = DartUtilities::dartToBool(DartUtilities::invokeUtilsMethod("isM
ap", 1, &value), exception); |
| 1156 ASSERT(!exception); |
| 1157 if (isMap) |
| 1158 return mapToV8(domData, value, map, exception); |
| 1159 |
| 1160 Dart_Handle maybeList = DartUtilities::invokeUtilsMethod("toListIfIterable",
1, &value); |
| 1161 if (Dart_IsList(maybeList)) |
| 1162 return listToV8(domData, maybeList, map, exception); |
| 1163 |
| 1164 v8::Handle<v8::Value> ret = JsInterop::fromDart(domData, value, exception); |
| 1165 map.set(value, ret); |
| 1166 return ret; |
| 1167 } |
| 1168 |
| 1169 static void jsifyCallback(Dart_NativeArguments args) |
| 1170 { |
| 1171 Dart_Handle exception = 0; |
| 1172 { |
| 1173 JsInteropScopes scopes(args); |
| 1174 Dart_Handle value = Dart_GetNativeArgument(args, 0); |
| 1175 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 1176 DartHandleToV8Map map; |
| 1177 v8::Local<v8::Value> ret = jsifyHelper(domData, value, map, exception); |
| 1178 if (exception) |
| 1179 goto fail; |
| 1180 |
| 1181 if (scopes.handleJsException(&exception)) |
| 1182 goto fail; |
| 1183 // Intentionally skip auto-conversion in this case as the user expects |
| 1184 // a JSObject. FIXME: evaluate if this is the right solution. |
| 1185 // Alternately, we could throw an exception. |
| 1186 if (ret->IsObject()) { |
| 1187 scopes.setReturnValue(JsObject::toDart(ret.As<v8::Object>())); |
| 1188 } else { |
| 1189 // This will throw an exception in Dart checked mode. |
| 1190 scopes.setReturnValue(ret); |
| 1191 } |
| 1192 return; |
| 1193 } |
| 1194 fail: |
| 1195 Dart_ThrowException(exception); |
| 1196 ASSERT_NOT_REACHED(); |
| 1197 } |
| 1198 |
| 1199 static void withThisCallback(Dart_NativeArguments args) |
| 1200 { |
| 1201 Dart_Handle exception = 0; |
| 1202 { |
| 1203 JsInteropScopes scopes(args); |
| 1204 Dart_Handle function = Dart_GetNativeArgument(args, 0); |
| 1205 DartDOMData* domData = static_cast<DartDOMData*>(Dart_GetNativeIsolateDa
ta(args)); |
| 1206 ASSERT(DartUtilities::isFunction(domData, function)); |
| 1207 |
| 1208 v8::Local<v8::Object> proxy = dartFunctionTemplate()->InstanceTemplate()
->NewInstance(); |
| 1209 DartHandleProxy::writePointerToProxy(proxy, function); |
| 1210 |
| 1211 v8::Local<v8::Function> ret = v8::Local<v8::Function>::Cast(domData->jsI
nteropData()->captureThisFunction()->Call(proxy, 0, 0)); |
| 1212 |
| 1213 if (scopes.handleJsException(&exception)) |
| 1214 goto fail; |
| 1215 scopes.setReturnValue(ret); |
| 1216 return; |
| 1217 } |
| 1218 fail: |
| 1219 Dart_ThrowException(exception); |
| 1220 ASSERT_NOT_REACHED(); |
| 1221 } |
| 1222 |
| 1223 } |
| 1224 |
| 1225 static DartNativeEntry nativeEntries[] = { |
| 1226 { JsInteropInternal::jsObjectConstructorCallback, 2, "JsObject_constructorCa
llback" }, |
| 1227 { JsInterop::jsInteropContextCallback, 0, "Js_context_Callback" }, |
| 1228 { JsInteropInternal::finalizeJsInterfacesCallback, 0, "Js_finalizeJsInterfac
es" }, |
| 1229 { JsInteropInternal::interfacesFinalizedCallback, 0, "Js_interfacesFinalized
_Callback" }, |
| 1230 { JsInteropInternal::jsifyCallback, 1, "JsObject_jsify" }, |
| 1231 { JsInteropInternal::withThisCallback, 1, "JsFunction_withThis" }, |
| 1232 { JsInterop::jsInteropGetterCallback, 2, "JsObject_[]" }, |
| 1233 { JsInteropInternal::setterCallback, 3, "JsObject_[]=" }, |
| 1234 { JsInteropInternal::hashCodeCallback, 1, "JsObject_hashCode" }, |
| 1235 { JsInterop::jsInteropCallMethodCallback, 3, "JsObject_callMethod" }, |
| 1236 { JsInteropInternal::toStringCallback, 1, "JsObject_toString" }, |
| 1237 { JsInteropInternal::identityEqualityCallback, 2, "JsObject_identityEquality
" }, |
| 1238 { JsInteropInternal::hasPropertyCallback, 2, "JsObject_hasProperty" }, |
| 1239 { JsInteropInternal::deletePropertyCallback, 2, "JsObject_deleteProperty" }, |
| 1240 { JsInteropInternal::instanceofCallback, 2, "JsObject_instanceof" }, |
| 1241 { JsInteropInternal::applyCallback, 3, "JsFunction_apply" }, |
| 1242 { JsInteropInternal::applyDebuggerOnlyCallback, 3, "JsFunction_applyDebugger
Only" }, |
| 1243 { JsInteropInternal::newJsArrayCallback, 0, "JsArray_newJsArray" }, |
| 1244 { JsInteropInternal::newJsArrayFromSafeListCallback, 1, "JsArray_newJsArrayF
romSafeList" }, |
| 1245 { JsInteropInternal::jsArrayLengthCallback, 1, "JsArray_length" }, |
| 1246 { JsInteropInternal::fromBrowserObjectCallback, 1, "JsObject_fromBrowserObje
ct" }, |
| 1247 { 0, 0, 0 }, |
| 1248 }; |
| 1249 |
| 1250 Dart_NativeFunction JsInterop::resolver(Dart_Handle nameHandle, int argumentCoun
t, bool* autoSetupScope) |
| 1251 { |
| 1252 ASSERT(autoSetupScope); |
| 1253 *autoSetupScope = true; |
| 1254 String name = DartUtilities::toString(nameHandle); |
| 1255 |
| 1256 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { |
| 1257 if (argumentCount == nativeEntries[i].argumentCount && name == nativeEnt
ries[i].name) { |
| 1258 return nativeEntries[i].nativeFunction; |
| 1259 } |
| 1260 } |
| 1261 |
| 1262 return 0; |
| 1263 } |
| 1264 |
| 1265 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) |
| 1266 { |
| 1267 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { |
| 1268 if (nf == nativeEntries[i].nativeFunction) { |
| 1269 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); |
| 1270 } |
| 1271 } |
| 1272 return 0; |
| 1273 } |
| 1274 |
| 1275 // Methods enabling a Dart List to emulate a JS Array. |
| 1276 static void indexedGetterArray(uint32_t index, const v8::PropertyCallbackInfo<v8
::Value>& info) |
| 1277 { |
| 1278 DartScopes scopes(info.Holder()); |
| 1279 Dart_Handle handle = scopes.handle; |
| 1280 DartDOMData* domData = DartDOMData::current(); |
| 1281 Dart_Handle ret = 0; |
| 1282 ASSERT(Dart_IsList(handle)); |
| 1283 ret = Dart_ListGetAt(handle, index); |
| 1284 if (Dart_IsError(ret)) { |
| 1285 // Return undefined if the index is invalid to match JS semantics. |
| 1286 // TODO(jacobr): we should log to the console warning of bad use of a |
| 1287 // Dart List. |
| 1288 return; |
| 1289 } |
| 1290 setJsReturnValue(domData, info, ret); |
| 1291 } |
| 1292 |
| 1293 static void indexedSetterArray(uint32_t index, v8::Local<v8::Value> value, const
v8::PropertyCallbackInfo<v8::Value>& info) |
| 1294 { |
| 1295 DartScopes scopes(info.Holder()); |
| 1296 Dart_Handle handle = scopes.handle; |
| 1297 DartDOMData* domData = DartDOMData::current(); |
| 1298 |
| 1299 Dart_Handle ret = 0; |
| 1300 ASSERT(Dart_IsList(handle)); |
| 1301 intptr_t length = 0; |
| 1302 ret = Dart_ListLength(handle, &length); |
| 1303 ASSERT(!Dart_IsError(ret)); |
| 1304 if (index >= static_cast<uint32_t>(length)) { |
| 1305 // Approximate JS semantics by filling the list with nulls if we |
| 1306 // attempt to add an element past the end of the list. |
| 1307 // TODO(jacobr): ideally we would match JS semantics exactly and fill |
| 1308 // the list with undefined. |
| 1309 Dart_Handle args[2] = { handle, DartUtilities::unsignedToDart(index + 1)
}; |
| 1310 ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_arra
yExtend"), 2, args); |
| 1311 ASSERT(!Dart_IsError(ret)); |
| 1312 } |
| 1313 ret = Dart_ListSetAt(handle, index, DartHandleProxy::unwrapValue(value)); |
| 1314 if (Dart_IsError(ret)) { |
| 1315 // Return undefined if the index is invalid to match JS semantics. |
| 1316 // TODO(jacobr): we should log to the console warning of bad use of a |
| 1317 // Dart list or add a JS expando to the object ala JS. |
| 1318 } |
| 1319 setJsReturnValue(domData, info, ret); |
| 1320 } |
| 1321 |
| 1322 static void indexedEnumeratorArray(const v8::PropertyCallbackInfo<v8::Array>& in
fo) |
| 1323 { |
| 1324 DartScopes scopes(info.Holder()); |
| 1325 Dart_Handle handle = scopes.handle; |
| 1326 |
| 1327 intptr_t length = 0; |
| 1328 ASSERT(Dart_IsList(handle)); |
| 1329 Dart_ListLength(handle, &length); |
| 1330 |
| 1331 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1332 v8::Local<v8::Array> indexes = v8::Array::New(v8Isolate, length); |
| 1333 for (int i = 0; i < length; i++) |
| 1334 indexes->Set(i, v8::Integer::New(v8Isolate, i)); |
| 1335 |
| 1336 v8SetReturnValue(info, indexes); |
| 1337 } |
| 1338 |
| 1339 v8::Handle<v8::Array> shallowListToV8(Dart_Handle value, DartDOMData* domData, D
art_Handle& exception) |
| 1340 { |
| 1341 ASSERT(Dart_IsList(value)); |
| 1342 |
| 1343 intptr_t length = 0; |
| 1344 Dart_Handle result = Dart_ListLength(value, &length); |
| 1345 if (!DartUtilities::checkResult(result, exception)) |
| 1346 return v8::Handle<v8::Array>(); |
| 1347 |
| 1348 v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), lengt
h); |
| 1349 |
| 1350 for (intptr_t i = 0; i < length; ++i) { |
| 1351 result = Dart_ListGetAt(value, i); |
| 1352 v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result, exc
eption); |
| 1353 // TODO(jacobr): is there a better way to handle this error case? |
| 1354 if (exception) |
| 1355 return v8::Handle<v8::Array>(); |
| 1356 array->Set(i, v8value); |
| 1357 } |
| 1358 return array; |
| 1359 } |
| 1360 |
| 1361 // If one of the methods we added to the Dart List proxy to make it mascarade |
| 1362 // as JavaScript Array happens to get called on a JavaScript object instead of |
| 1363 // a Dart List proxy object we use the corresponding regular JavaScript Array |
| 1364 // method which will do something plausible in most cases even if the |
| 1365 // JavaScript object isn't actually a JavaScript array. The chrome devtools |
| 1366 // appear to go down exactly this code path as I got crashes in the devtools |
| 1367 // due to info.Holder not being a DartHandleProxy. I haven't investigated |
| 1368 // whether devtools would still function properly if we threw an exception |
| 1369 // which would match the behavior of DOM classes when a method is called on the |
| 1370 // wrong object. The current behavior while strange matches the behavior |
| 1371 // observed when an method from Array is called on an arbitrary JS object. |
| 1372 bool handleNonDartProxyThis(const v8::FunctionCallbackInfo<v8::Value>& info, con
st char* jsMethodName) |
| 1373 { |
| 1374 if (DartHandleProxy::isDartProxy(info.Holder())) { |
| 1375 return false; |
| 1376 } |
| 1377 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1378 // Get the method on JS array in an inefficient way. |
| 1379 v8::Local<v8::Function> method = v8::Array::New(v8Isolate)->Get(v8::String::
NewFromUtf8(v8Isolate, jsMethodName)).As<v8::Function>(); |
| 1380 ASSERT(!method.IsEmpty()); |
| 1381 int length = info.Length(); |
| 1382 Vector<v8::Local<v8::Value> > v8Args(length); |
| 1383 for (int i = 0; i < length; ++i) { |
| 1384 v8Args[i] = info[i]; |
| 1385 } |
| 1386 v8SetReturnValue(info, method->Call(info.Holder(), length, v8Args.data())); |
| 1387 return true; |
| 1388 } |
| 1389 |
| 1390 void arrayHelper(const v8::FunctionCallbackInfo<v8::Value>& info, const char* me
thodName, const char* jsMethodName) |
| 1391 { |
| 1392 if (handleNonDartProxyThis(info, jsMethodName)) { |
| 1393 return; |
| 1394 } |
| 1395 DartScopes scopes(info.Holder()); |
| 1396 DartDOMData* domData = DartDOMData::current(); |
| 1397 Dart_Handle handle = scopes.handle; |
| 1398 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin
g(methodName), 1, &handle); |
| 1399 setJsReturnValue(domData, info, ret); |
| 1400 } |
| 1401 |
| 1402 void arrayHelper1Arg(const v8::FunctionCallbackInfo<v8::Value>& info, const char
* methodName, const char* jsMethodName) |
| 1403 { |
| 1404 if (handleNonDartProxyThis(info, jsMethodName)) { |
| 1405 return; |
| 1406 } |
| 1407 DartScopes scopes(info.Holder()); |
| 1408 DartDOMData* domData = DartDOMData::current(); |
| 1409 Dart_Handle handle = scopes.handle; |
| 1410 Dart_Handle e; |
| 1411 if (info.Length() == 0) { |
| 1412 e = Dart_Null(); |
| 1413 } else { |
| 1414 e = JsInterop::toDart(info[0]); |
| 1415 } |
| 1416 Dart_Handle args[2] = { handle, e }; |
| 1417 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin
g(methodName), 2, args); |
| 1418 setJsReturnValue(domData, info, ret); |
| 1419 } |
| 1420 |
| 1421 void arrayHelperWithArgsAsList(const v8::FunctionCallbackInfo<v8::Value>& info,
const char* methodName, const char* jsMethodName) |
| 1422 { |
| 1423 if (handleNonDartProxyThis(info, jsMethodName)) { |
| 1424 return; |
| 1425 } |
| 1426 DartScopes scopes(info.Holder()); |
| 1427 DartDOMData* domData = DartDOMData::current(); |
| 1428 Dart_Handle handle = scopes.handle; |
| 1429 int length = info.Length(); |
| 1430 Dart_Handle argsList = Dart_NewList(length); |
| 1431 for (int i = 0; i < length; ++i) { |
| 1432 Dart_ListSetAt(argsList, i, JsInterop::toDart(info[i])); |
| 1433 } |
| 1434 // Note: this is also just info.Holder(). |
| 1435 Dart_Handle args[2] = { handle, argsList }; |
| 1436 setJsReturnValue(domData, info, Dart_Invoke(domData->jsLibrary(), Dart_NewSt
ringFromCString(methodName), 2, args)); |
| 1437 } |
| 1438 |
| 1439 static void arrayNamedPropertyGetter(v8::Local<v8::String> name, const v8::Prope
rtyCallbackInfo<v8::Value>& info) |
| 1440 { |
| 1441 if (!DartHandleProxy::isDartProxy(info.Holder())) { |
| 1442 // I don't think this case can occur but avoid crashing if there is an |
| 1443 // exotic way to trigger it. |
| 1444 return; |
| 1445 } |
| 1446 |
| 1447 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1448 if (!name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) |
| 1449 return; |
| 1450 |
| 1451 DartScopes scopes(info.Holder()); |
| 1452 Dart_Handle handle = scopes.handle; |
| 1453 |
| 1454 intptr_t length = 0; |
| 1455 Dart_ListLength(handle, &length); |
| 1456 v8SetReturnValueInt(info, length); |
| 1457 } |
| 1458 |
| 1459 static void arrayNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v
8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) |
| 1460 { |
| 1461 if (!DartHandleProxy::isDartProxy(info.Holder())) { |
| 1462 // I don't think this case can occur but avoid crashing if there is an |
| 1463 // exotic way to trigger it. |
| 1464 return; |
| 1465 } |
| 1466 |
| 1467 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1468 if (!property->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { |
| 1469 return; |
| 1470 } |
| 1471 |
| 1472 DartScopes scopes(info.Holder()); |
| 1473 Dart_Handle handle = scopes.handle; |
| 1474 DartDOMData* domData = DartDOMData::current(); |
| 1475 Dart_Handle args[2] = { handle, JsInterop::toDart(value) }; |
| 1476 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin
g("_setListLength"), 2, args); |
| 1477 setJsReturnValue(domData, info, ret); |
| 1478 } |
| 1479 |
| 1480 static void arrayQueryProperty(v8::Local<v8::String> name, const v8::PropertyCal
lbackInfo<v8::Integer>& info) |
| 1481 { |
| 1482 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1483 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { |
| 1484 v8SetReturnValueInt(info, v8::DontEnum | v8::DontDelete); |
| 1485 } |
| 1486 } |
| 1487 |
| 1488 static void arrayDeleteProperty(v8::Local<v8::String> name, const v8::PropertyCa
llbackInfo<v8::Boolean>& info) |
| 1489 { |
| 1490 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1491 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) { |
| 1492 v8SetReturnValueBool(info, false); |
| 1493 } |
| 1494 } |
| 1495 |
| 1496 void arrayToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1497 { |
| 1498 arrayHelper(info, "_arrayToString", "toString"); |
| 1499 } |
| 1500 |
| 1501 |
| 1502 void arrayJoinCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1503 { |
| 1504 arrayHelper1Arg(info, "_arrayJoin", "join"); |
| 1505 } |
| 1506 |
| 1507 void arrayPushCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1508 { |
| 1509 arrayHelperWithArgsAsList(info, "_arrayPush", "push"); |
| 1510 } |
| 1511 |
| 1512 void arrayPopCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1513 { |
| 1514 arrayHelper(info, "_arrayPop", "pop"); |
| 1515 } |
| 1516 |
| 1517 void concatCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1518 { |
| 1519 arrayHelperWithArgsAsList(info, "_arrayConcat", "concat"); |
| 1520 } |
| 1521 |
| 1522 void arrayReverseCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1523 { |
| 1524 arrayHelper(info, "_arrayReverse", "reverse"); |
| 1525 } |
| 1526 |
| 1527 void arrayShiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1528 { |
| 1529 arrayHelper(info, "_arrayShift", "shift"); |
| 1530 } |
| 1531 |
| 1532 void arrayUnshiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1533 { |
| 1534 arrayHelperWithArgsAsList(info, "_arrayUnshift", "unshift"); |
| 1535 } |
| 1536 |
| 1537 void arraySpliceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1538 { |
| 1539 arrayHelperWithArgsAsList(info, "_arraySplice", "splice"); |
| 1540 } |
| 1541 |
| 1542 void toJsArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1543 { |
| 1544 // This isn't a method on the JavaScript Array class so it is fine to |
| 1545 // just return the existing hopefully Array like JS object if not called on |
| 1546 // a Dart List. |
| 1547 if (!DartHandleProxy::isDartProxy(info.Holder())) { |
| 1548 v8SetReturnValue(info, info.Holder()); |
| 1549 } |
| 1550 DartScopes scopes(info.Holder()); |
| 1551 DartDOMData* domData = DartDOMData::current(); |
| 1552 Dart_Handle handle = scopes.handle; |
| 1553 Dart_Handle exception = 0; |
| 1554 v8::Local<v8::Array> v8Array = shallowListToV8(handle, domData, exception); |
| 1555 ASSERT(!exception); |
| 1556 v8SetReturnValue(info, v8Array); |
| 1557 } |
| 1558 |
| 1559 void arraySortCallback(const v8::FunctionCallbackInfo<v8::Value>& info) |
| 1560 { |
| 1561 // TODO(jacobr): consider using the JavaScript sort method instead. |
| 1562 arrayHelper1Arg(info, "_arraySort", "sort"); |
| 1563 } |
| 1564 |
| 1565 v8::Local<v8::FunctionTemplate> dartListTemplate() |
| 1566 { |
| 1567 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ())
; |
| 1568 v8::Local<v8::FunctionTemplate> proxyTemplateLocal; |
| 1569 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent(); |
| 1570 if (proxyTemplate.IsEmpty()) { |
| 1571 proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New
(v8Isolate)); |
| 1572 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro
xyTemplate); |
| 1573 // Set to Array because we want these instances to appear to be |
| 1574 // JavaScript arrays as far as user code is concerned. |
| 1575 proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Arr
ay")); |
| 1576 // Hack to set the prototype to be the prototype of an actual JavaScript
Array. |
| 1577 |
| 1578 v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->Proto
typeTemplate(); |
| 1579 |
| 1580 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8::
FunctionTemplate::New(v8Isolate, arrayToStringCallback), v8::DontEnum); |
| 1581 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "join"), v8::Func
tionTemplate::New(v8Isolate, arrayJoinCallback), v8::DontEnum); |
| 1582 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "push"), v8::Func
tionTemplate::New(v8Isolate, arrayPushCallback), v8::DontEnum); |
| 1583 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "pop"), v8::Funct
ionTemplate::New(v8Isolate, arrayPopCallback), v8::DontEnum); |
| 1584 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "concat"), v8::Fu
nctionTemplate::New(v8Isolate, concatCallback), v8::DontEnum); |
| 1585 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "reverse"), v8::F
unctionTemplate::New(v8Isolate, arrayReverseCallback), v8::DontEnum); |
| 1586 |
| 1587 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "shift"), v8::Fun
ctionTemplate::New(v8Isolate, arrayShiftCallback), v8::DontEnum); |
| 1588 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "unshift"), v8::F
unctionTemplate::New(v8Isolate, arrayUnshiftCallback), v8::DontEnum); |
| 1589 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "splice"), v8::Fu
nctionTemplate::New(v8Isolate, arraySpliceCallback), v8::DontEnum); |
| 1590 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "sort"), v8::Func
tionTemplate::New(v8Isolate, arraySortCallback), v8::DontEnum); |
| 1591 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "$toJsArray"), v8
::FunctionTemplate::New(v8Isolate, toJsArrayCallback), v8::DontEnum); |
| 1592 |
| 1593 // ES6 experimental properties not currently supported that we could sup
port if needed. |
| 1594 // These would require building separate live proxy objects. |
| 1595 // "entries", |
| 1596 // "values", |
| 1597 // "keys" |
| 1598 |
| 1599 v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(p
roxyTemplateLocal); |
| 1600 instanceTemplate->SetIndexedPropertyHandler(&indexedGetterArray, &indexe
dSetterArray, 0, 0, &indexedEnumeratorArray); |
| 1601 instanceTemplate->SetNamedPropertyHandler(&arrayNamedPropertyGetter, &ar
rayNamedPropertySetter, &arrayQueryProperty, &arrayDeleteProperty, 0); |
| 1602 } else { |
| 1603 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro
xyTemplate); |
| 1604 } |
| 1605 return proxyTemplateLocal; |
| 1606 } |
| 1607 |
| 1608 void JsInterop::jsInteropGetterCallback(Dart_NativeArguments args) |
| 1609 { |
| 1610 JsInteropInternal::getterCallback(args); |
| 1611 } |
| 1612 |
| 1613 void JsInterop::jsInteropCallMethodCallback(Dart_NativeArguments args) |
| 1614 { |
| 1615 JsInteropInternal::callMethodCallback(args); |
| 1616 } |
| 1617 |
| 1618 void JsInterop::jsInteropContextCallback(Dart_NativeArguments args) |
| 1619 { |
| 1620 return JsInteropInternal::contextCallback(args); |
| 1621 } |
| 1622 |
| 1623 } |
OLD | NEW |