Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: Source/bindings/core/dart/DartJsInterop.cpp

Issue 1177953010: Support new style JS interop (Closed) Base URL: svn://svn.chromium.org/blink/branches/dart/dartium
Patch Set: ptal. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
1048 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf) 1205 const uint8_t* JsInterop::symbolizer(Dart_NativeFunction nf)
1049 { 1206 {
1050 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) { 1207 for (intptr_t i = 0; nativeEntries[i].nativeFunction != 0; i++) {
1051 if (nf == nativeEntries[i].nativeFunction) { 1208 if (nf == nativeEntries[i].nativeFunction) {
1052 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name); 1209 return reinterpret_cast<const uint8_t*>(nativeEntries[i].name);
1053 } 1210 }
1054 } 1211 }
1055 return 0; 1212 return 0;
1056 } 1213 }
1057 1214
1058 } 1215 // Methods enabling a Dart List to emulate a JS Array.
1216 static void indexedGetterArray(uint32_t index, const v8::PropertyCallbackInfo<v8 ::Value>& info)
1217 {
1218 DartScopes scopes(info.Holder());
1219 Dart_Handle handle = scopes.handle;
1220 DartDOMData* domData = DartDOMData::current();
1221 Dart_Handle ret = 0;
1222 ASSERT(Dart_IsList(handle));
1223 ret = Dart_ListGetAt(handle, index);
1224 if (Dart_IsError(ret)) {
1225 // Return undefined if the index is invalid to match JS semantics.
1226 // TODO(jacobr): we should log to the console warning of bad use of a
1227 // Dart List.
1228 return;
1229 }
1230 setJsReturnValue(domData, info, ret);
1231 }
1232
1233 static void indexedSetterArray(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
1234 {
1235 DartScopes scopes(info.Holder());
1236 Dart_Handle handle = scopes.handle;
1237 DartDOMData* domData = DartDOMData::current();
1238
1239 Dart_Handle ret = 0;
1240 ASSERT(Dart_IsList(handle));
1241 intptr_t length = 0;
1242 ret = Dart_ListLength(handle, &length);
1243 ASSERT(!Dart_IsError(ret));
1244 if (index >= length) {
1245 // Approximate JS semantics by filling the list with nulls if we
1246 // attempt to add an element past the end of the list.
1247 // TODO(jacobr): ideally we would match JS semantics exactly and fill
1248 // the list with undefined.
1249 Dart_Handle args[2] = { handle, DartUtilities::unsignedToDart(index + 1) };
1250 ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCString("_arra yExtend"), 2, args);
1251 ASSERT(!Dart_IsError(ret));
1252 }
1253 ret = Dart_ListSetAt(handle, index, DartHandleProxy::unwrapValue(value));
1254 if (Dart_IsError(ret)) {
1255 // Return undefined if the index is invalid to match JS semantics.
1256 // TODO(jacobr): we should log to the console warning of bad use of a
1257 // Dart list or add a JS expando to the object ala JS.
1258 }
1259 setJsReturnValue(domData, info, ret);
1260 }
1261
1262 static void indexedEnumeratorArray(const v8::PropertyCallbackInfo<v8::Array>& in fo)
1263 {
1264 DartScopes scopes(info.Holder());
1265 Dart_Handle handle = scopes.handle;
1266
1267 intptr_t length = 0;
1268 ASSERT(Dart_IsList(handle));
1269 Dart_ListLength(handle, &length);
1270
1271 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
1272 v8::Local<v8::Array> indexes = v8::Array::New(v8Isolate, length);
1273 for (int i = 0; i < length; i++)
1274 indexes->Set(i, v8::Integer::New(v8Isolate, i));
1275
1276 v8SetReturnValue(info, indexes);
1277 }
1278
1279 v8::Handle<v8::Array> shallowListToV8(Dart_Handle value, DartDOMData* domData, D art_Handle& exception)
1280 {
1281 ASSERT(Dart_IsList(value));
1282
1283 intptr_t length = 0;
1284 Dart_Handle result = Dart_ListLength(value, &length);
1285 if (!DartUtilities::checkResult(result, exception))
1286 return v8::Handle<v8::Array>();
1287
1288 v8::Local<v8::Array> array = v8::Array::New(v8::Isolate::GetCurrent(), lengt h);
1289
1290 for (intptr_t i = 0; i < length; ++i) {
1291 result = Dart_ListGetAt(value, i);
1292 v8::Handle<v8::Value> v8value = JsInterop::fromDart(domData, result, exc eption);
1293 // TODO(jacobr): is there a better way to handle this error case?
1294 if (exception)
1295 return v8::Handle<v8::Array>();
1296 array->Set(i, v8value);
1297 }
1298 return array;
1299 }
1300
1301 // If one of the methods we added to the Dart List proxy to make it mascarade
1302 // as JavaScript Array happens to get called on a JavaScript object instead of
1303 // a Dart List proxy object we use the corresponding regular JavaScript Array
1304 // method which will do something plausible in most cases even if the
1305 // JavaScript object isn't actually a JavaScript array.
1306 bool handleNonDartProxyThis(const v8::FunctionCallbackInfo<v8::Value>& info, con st char* jsMethodName)
1307 {
1308 if (DartHandleProxy::isDartProxy(info.Holder())) {
1309 return false;
1310 }
1311 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
vsm 2015/06/29 13:49:26 Do we actually ever get down this path? And if so
Jacob 2015/06/29 17:56:08 Yeah it is a bit strange but I'm trying to make th
1312 // Get the method on JS array in an inefficient way.
1313 v8::Local<v8::Function> method = v8::Array::New(v8Isolate)->Get(v8::String:: NewFromUtf8(v8Isolate, jsMethodName)).As<v8::Function>();
1314 ASSERT(!method.IsEmpty());
1315 int length = info.Length();
1316 Vector<v8::Local<v8::Value> > v8Args(length);
1317 for (int i = 0; i < length; ++i) {
1318 v8Args[i] = info[i];
1319 }
1320 v8SetReturnValue(info, method->Call(info.Holder(), length, v8Args.data()));
1321 return true;
1322 }
1323
1324 void arrayHelper(const v8::FunctionCallbackInfo<v8::Value>& info, const char* me thodName, const char* jsMethodName)
1325 {
1326 if (handleNonDartProxyThis(info, jsMethodName)) {
1327 return;
1328 }
1329 DartScopes scopes(info.Holder());
1330 DartDOMData* domData = DartDOMData::current();
1331 Dart_Handle handle = scopes.handle;
1332 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 1, &handle);
1333 setJsReturnValue(domData, info, ret);
1334 }
1335
1336 void arrayHelper1Arg(const v8::FunctionCallbackInfo<v8::Value>& info, const char * methodName, const char* jsMethodName)
1337 {
1338 if (handleNonDartProxyThis(info, jsMethodName)) {
1339 return;
1340 }
1341 DartScopes scopes(info.Holder());
1342 DartDOMData* domData = DartDOMData::current();
1343 Dart_Handle handle = scopes.handle;
1344 Dart_Handle e;
1345 if (info.Length() == 0) {
1346 e = Dart_Null();
1347 } else {
1348 e = JsInterop::toDart(info[0]);
1349 }
1350 Dart_Handle args[2] = { handle, e };
1351 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g(methodName), 2, args);
1352 setJsReturnValue(domData, info, ret);
1353 }
1354
1355 void arrayHelperWithArgsAsList(const v8::FunctionCallbackInfo<v8::Value>& info, const char* methodName, const char* jsMethodName)
1356 {
1357 if (handleNonDartProxyThis(info, jsMethodName)) {
1358 return;
1359 }
1360 DartScopes scopes(info.Holder());
1361 DartDOMData* domData = DartDOMData::current();
1362 Dart_Handle handle = scopes.handle;
1363 int length = info.Length();
1364 Dart_Handle argsList = Dart_NewList(length);
1365 for (int i = 0; i < length; ++i) {
1366 Dart_ListSetAt(argsList, i, JsInterop::toDart(info[i]));
1367 }
1368 // Note: this is also just info.Holder().
1369 Dart_Handle args[2] = { handle, argsList };
1370 setJsReturnValue(domData, info, Dart_Invoke(domData->jsLibrary(), Dart_NewSt ringFromCString(methodName), 2, args));
1371 }
1372
1373 static void arrayNamedPropertyGetter(v8::Local<v8::String> name, const v8::Prope rtyCallbackInfo<v8::Value>& info)
1374 {
1375 if (!DartHandleProxy::isDartProxy(info.Holder())) {
1376 // I don't think this case can occur but avoid crashing if there is an
1377 // exotic way to trigger it.
1378 return;
1379 }
1380
1381 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
1382 if (!name->Equals(v8::String::NewFromUtf8(v8Isolate, "length")))
1383 return;
1384
1385 DartScopes scopes(info.Holder());
1386 Dart_Handle handle = scopes.handle;
1387
1388 intptr_t length = 0;
1389 Dart_ListLength(handle, &length);
1390 v8SetReturnValueInt(info, length);
1391 }
1392
1393 static void arrayNamedPropertySetter(v8::Local<v8::String> property, v8::Local<v 8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
1394 {
1395 if (!DartHandleProxy::isDartProxy(info.Holder())) {
1396 // I don't think this case can occur but avoid crashing if there is an
1397 // exotic way to trigger it.
1398 return;
1399 }
1400
1401 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
1402 if (!property->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) {
1403 return;
1404 }
1405
1406 DartScopes scopes(info.Holder());
1407 Dart_Handle handle = scopes.handle;
1408 DartDOMData* domData = DartDOMData::current();
1409 Dart_Handle args[2] = { handle, JsInterop::toDart(value) };
1410 Dart_Handle ret = Dart_Invoke(domData->jsLibrary(), Dart_NewStringFromCStrin g("_setListLength"), 2, args);
1411 setJsReturnValue(domData, info, ret);
1412 }
1413
1414 static void arrayQueryProperty(v8::Local<v8::String> name, const v8::PropertyCal lbackInfo<v8::Integer>& info)
1415 {
1416 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
1417 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) {
1418 v8SetReturnValueInt(info, v8::DontEnum | v8::DontDelete);
1419 }
1420 }
1421
1422 static void arrayDeleteProperty(v8::Local<v8::String> name, const v8::PropertyCa llbackInfo<v8::Boolean>& info)
1423 {
1424 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
1425 if (name->Equals(v8::String::NewFromUtf8(v8Isolate, "length"))) {
1426 v8SetReturnValueBool(info, false);
1427 }
1428 }
1429
1430 void arrayToStringCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1431 {
1432 arrayHelper(info, "_arrayToString", "toString");
1433 }
1434
1435
1436 void arrayJoinCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1437 {
1438 arrayHelper1Arg(info, "_arrayJoin", "join");
1439 }
1440
1441 void arrayPushCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1442 {
1443 arrayHelperWithArgsAsList(info, "_arrayPush", "push");
1444 }
1445
1446 void arrayPopCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1447 {
1448 arrayHelper(info, "_arrayPop", "pop");
1449 }
1450
1451 void concatCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1452 {
1453 arrayHelperWithArgsAsList(info, "_arrayConcat", "concat");
1454 }
1455
1456 void arrayReverseCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1457 {
1458 arrayHelper(info, "_arrayReverse", "reverse");
1459 }
1460
1461 void arrayShiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1462 {
1463 arrayHelper(info, "_arrayShift", "shift");
1464 }
1465
1466 void arrayUnshiftCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1467 {
1468 arrayHelperWithArgsAsList(info, "_arrayUnshift", "unshift");
1469 }
1470
1471 void arraySpliceCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1472 {
1473 arrayHelperWithArgsAsList(info, "_arraySplice", "splice");
1474 }
1475
1476 void toJsArrayCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1477 {
1478 // This isn't a method on the JavaScript Array class so it is fine to
1479 // just return the existing hopefully Array like JS object if not called on
1480 // a Dart List.
1481 if (!DartHandleProxy::isDartProxy(info.Holder())) {
1482 v8SetReturnValue(info, info.Holder());
1483 }
1484 DartScopes scopes(info.Holder());
1485 DartDOMData* domData = DartDOMData::current();
1486 Dart_Handle handle = scopes.handle;
1487 Dart_Handle exception = 0;
1488 v8::Local<v8::Array> v8Array = shallowListToV8(handle, domData, exception);
1489 ASSERT(!exception);
1490 v8SetReturnValue(info, v8Array);
1491 }
1492
1493 void arraySortCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
1494 {
1495 // TODO(jacobr): consider using the JavaScript sort method instead.
1496 arrayHelper1Arg(info, "_arraySort", "sort");
1497 }
1498
1499 v8::Local<v8::FunctionTemplate> dartListTemplate()
1500 {
1501 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, proxyTemplate, ()) ;
1502 v8::Local<v8::FunctionTemplate> proxyTemplateLocal;
1503 v8::Isolate* v8Isolate = v8::Isolate::GetCurrent();
1504 if (proxyTemplate.IsEmpty()) {
1505 proxyTemplate.Reset(v8::Isolate::GetCurrent(), v8::FunctionTemplate::New (v8Isolate));
1506 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate);
1507 // Set to Array because we want these instances to appear to be
1508 // JavaScript arrays as far as user code is concerned.
1509 proxyTemplateLocal->SetClassName(v8::String::NewFromUtf8(v8Isolate, "Arr ay"));
1510 // Hack to set the prototype to be the prototype of an actual JavaScript Array.
1511
1512 v8::Local<v8::ObjectTemplate> protoTemplate = proxyTemplateLocal->Proto typeTemplate();
1513
1514 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "toString"), v8:: FunctionTemplate::New(v8Isolate, arrayToStringCallback), v8::DontEnum);
1515 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "join"), v8::Func tionTemplate::New(v8Isolate, arrayJoinCallback), v8::DontEnum);
1516 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "push"), v8::Func tionTemplate::New(v8Isolate, arrayPushCallback), v8::DontEnum);
1517 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "pop"), v8::Funct ionTemplate::New(v8Isolate, arrayPopCallback), v8::DontEnum);
1518 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "concat"), v8::Fu nctionTemplate::New(v8Isolate, concatCallback), v8::DontEnum);
1519 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "reverse"), v8::F unctionTemplate::New(v8Isolate, arrayReverseCallback), v8::DontEnum);
1520
1521 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "shift"), v8::Fun ctionTemplate::New(v8Isolate, arrayShiftCallback), v8::DontEnum);
1522 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "unshift"), v8::F unctionTemplate::New(v8Isolate, arrayUnshiftCallback), v8::DontEnum);
1523 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "splice"), v8::Fu nctionTemplate::New(v8Isolate, arraySpliceCallback), v8::DontEnum);
1524 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "sort"), v8::Func tionTemplate::New(v8Isolate, arraySortCallback), v8::DontEnum);
1525 protoTemplate ->Set(v8::String::NewFromUtf8(v8Isolate, "$toJsArray"), v8 ::FunctionTemplate::New(v8Isolate, toJsArrayCallback), v8::DontEnum);
1526
1527 // ES6 experimental properties not currently supported that we could sup port if needed.
1528 // These would require building separate live proxy objects.
1529 // "entries",
1530 // "values",
1531 // "keys"
1532
1533 v8::Local<v8::ObjectTemplate> instanceTemplate = setupInstanceTemplate(p roxyTemplateLocal);
1534 instanceTemplate->SetIndexedPropertyHandler(&indexedGetterArray, &indexe dSetterArray, 0, 0, &indexedEnumeratorArray);
1535 instanceTemplate->SetNamedPropertyHandler(&arrayNamedPropertyGetter, &ar rayNamedPropertySetter, &arrayQueryProperty, &arrayDeleteProperty, 0);
1536 } else {
1537 proxyTemplateLocal = v8::Local<v8::FunctionTemplate>::New(v8Isolate, pro xyTemplate);
1538 }
1539 return proxyTemplateLocal;
1540 }
1541
1542 }
OLDNEW
« no previous file with comments | « Source/bindings/core/dart/DartInjectedScript.cpp ('k') | Source/bindings/core/dart/DartJsInteropData.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698