Skip to content

Commit 66ba800

Browse files
authored
[java-shell] JavaServiceProvider should return only JS objects so they support 'defineProperty' (#372)
1 parent 15d286d commit 66ba800

File tree

4 files changed

+44
-50
lines changed

4 files changed

+44
-50
lines changed

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/MongoShellContext.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import org.graalvm.polyglot.Context
1515
import org.graalvm.polyglot.Source
1616
import org.graalvm.polyglot.Value
1717
import org.graalvm.polyglot.proxy.ProxyExecutable
18-
import org.graalvm.polyglot.proxy.ProxyObject
19-
import org.graalvm.polyglot.proxy.ProxyObject.fromMap
2018
import org.intellij.lang.annotations.Language
2119
import java.io.Closeable
2220
import java.time.LocalDateTime
@@ -34,7 +32,7 @@ import java.util.concurrent.TimeUnit
3432
import java.util.regex.Pattern
3533

3634
internal class MongoShellContext(client: MongoClient) : Closeable {
37-
private val ctx: Context = Context.create()
35+
val ctx: Context = Context.create()
3836
private val serviceProvider = JavaServiceProvider(client, this)
3937
private val shellEvaluator: Value
4038
private val bsonTypes: BsonTypes
@@ -227,6 +225,7 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
227225
v.fitsInLong() -> LongResult(v.asLong())
228226
v.fitsInFloat() -> FloatResult(v.asFloat())
229227
v.fitsInDouble() -> DoubleResult(v.asDouble())
228+
v.equalsTo("undefined") -> VoidResult
230229
v.isNull -> NullResult
231230
v.isHostObject && v.asHostObject<Any?>() is Unit -> VoidResult
232231
v.isHostObject && v.asHostObject<Any?>() is Document -> DocumentResult(v.asHostObject())
@@ -253,7 +252,7 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
253252

254253
fun <T> toJsPromise(promise: Either<T>): Value {
255254
return when (promise) {
256-
is Right -> evalInner("(v) => new Promise(((resolve) => resolve(v)))", "resolved_promise_script").execute(promise.value)
255+
is Right -> evalInner("(v) => new Promise(((resolve) => resolve(v)))", "resolved_promise_script").execute(toJs(promise.value))
257256
is Left -> evalInner("(v) => new Promise(((_, reject) => reject(v)))", "rejected_promise_script").execute(promise.value)
258257
}
259258
}
@@ -266,19 +265,23 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
266265

267266
private fun Value.instanceOf(@Language("js") clazz: String): Boolean = evalInner("(x) => x instanceof $clazz", "instance_of_script").execute(this).asBoolean()
268267

268+
private fun Value.equalsTo(@Language("js") value: String): Boolean = evalInner("(x) => x === $value", "equals_script").execute(this).asBoolean()
269+
269270
fun toJs(o: Any?): Any? {
270271
return when (o) {
271272
is Iterable<*> -> toJs(o)
272273
is Map<*, *> -> toJs(o)
274+
Unit -> evalInner("undefined")
273275
else -> o
274276
}
275277
}
276278

277-
private fun toJs(map: Map<*, *>): ProxyObject {
278-
val convertedMap: Map<String, Any?> = map.entries.asSequence()
279-
.filter { (key, _) -> key is String }
280-
.associate { e -> e.key as String to toJs(e.value) }
281-
return fromMap(convertedMap)
279+
private fun toJs(map: Map<*, *>): Value {
280+
val jsMap = evalInner("new Object()")
281+
for ((key, value) in map.entries) {
282+
jsMap.putMember(key as String, toJs(value))
283+
}
284+
return jsMap
282285
}
283286

284287
private fun toJs(list: Iterable<Any?>): Value {

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/service/JavaServiceProvider.kt

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import com.mongodb.client.result.UpdateResult
77
import com.mongodb.mongosh.MongoShellContext
88
import com.mongodb.mongosh.result.ArrayResult
99
import com.mongodb.mongosh.result.CommandException
10-
import com.mongodb.mongosh.result.DeleteResult
1110
import com.mongodb.mongosh.result.DocumentResult
1211
import org.bson.Document
1312
import org.graalvm.polyglot.HostAccess
@@ -25,9 +24,9 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
2524
override fun runCommand(database: String, spec: Value): Value = promise {
2625
getDatabase(database, null).map { db ->
2726
if (spec.isString) {
28-
context.toJs(db.runCommand(Document(spec.asString(), 1)))
27+
db.runCommand(Document(spec.asString(), 1))
2928
} else {
30-
context.toJs(db.runCommand(toDocument(spec, "spec")))
29+
db.runCommand(toDocument(spec, "spec"))
3130
}
3231
}
3332
}
@@ -44,7 +43,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
4443
if (!isOk) {
4544
throw Exception(res.toJson())
4645
}
47-
context.toJs(res)
46+
res
4847
}
4948
}
5049

@@ -54,9 +53,8 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
5453
val dbOptions = toDocument(dbOptions, "dbOptions")
5554
getDatabase(database, dbOptions).map { db ->
5655
db.getCollection(collection).insertOne(document)
57-
context.toJs(mapOf(
58-
"result" to mapOf("ok" to true),
59-
"insertedId" to "UNKNOWN"))
56+
mapOf("result" to mapOf("ok" to true),
57+
"insertedId" to "UNKNOWN")
6058
}
6159
}
6260

@@ -69,13 +67,11 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
6967
getDatabase(database, dbOptions).flatMap { db ->
7068
convert(ReplaceOptions(), replaceOptionsConverters, replaceOptionsDefaultConverters, options).map { options ->
7169
val res = db.getCollection(collection).replaceOne(filter, replacement, options)
72-
context.toJs(mapOf(
73-
"result" to mapOf("ok" to res.wasAcknowledged()),
70+
mapOf("result" to mapOf("ok" to res.wasAcknowledged()),
7471
"matchedCount" to res.matchedCount,
7572
"modifiedCount" to res.modifiedCount,
7673
"upsertedCount" to if (res.upsertedId == null) 0 else 1,
77-
"upsertedId" to res.upsertedId
78-
))
74+
"upsertedId" to res.upsertedId)
7975
}
8076
}
8177
}
@@ -95,11 +91,11 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
9591
update.hasMembers() -> Right(db.getCollection(collection).updateMany(filter, toDocument(update, "update"), updateOptions))
9692
else -> Left<UpdateResult>(IllegalArgumentException("updatePipeline must be a list or object"))
9793
}.map { res ->
98-
context.toJs(mapOf("result" to mapOf("ok" to res.wasAcknowledged()),
94+
mapOf("result" to mapOf("ok" to res.wasAcknowledged()),
9995
"matchedCount" to res.matchedCount,
10096
"modifiedCount" to res.modifiedCount,
10197
"upsertedCount" to if (res.upsertedId == null) 0 else 1,
102-
"upsertedId" to res.upsertedId))
98+
"upsertedId" to res.upsertedId)
10399
}
104100
}
105101
}
@@ -127,12 +123,11 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
127123
val pipeline = toList(update, "update")?.map { it as Document }
128124
coll.updateOne(filter, pipeline, updateOptions)
129125
} else coll.updateOne(filter, toDocument(update, "update"), updateOptions)
130-
context.toJs(mapOf("result" to mapOf("ok" to res.wasAcknowledged()),
126+
mapOf("result" to mapOf("ok" to res.wasAcknowledged()),
131127
"matchedCount" to res.matchedCount,
132128
"modifiedCount" to res.modifiedCount,
133129
"upsertedCount" to if (res.upsertedId == null) 0 else 1,
134-
"upsertedId" to res.upsertedId
135-
))
130+
"upsertedId" to res.upsertedId)
136131
}
137132
}
138133
}
@@ -164,15 +159,15 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
164159
val writeModels = requests.map { getWriteModel(it as Document) }
165160
unwrap(writeModels).map { requests ->
166161
val result = db.getCollection(collection).bulkWrite(requests, options)
167-
context.toJs(mapOf(
162+
mapOf(
168163
"result" to mapOf("ok" to result.wasAcknowledged()),
169164
"insertedCount" to result.insertedCount,
170165
"insertedIds" to "UNKNOWN",
171166
"matchedCount" to result.matchedCount,
172167
"modifiedCount" to result.modifiedCount,
173168
"deletedCount" to result.deletedCount,
174169
"upsertedCount" to result.upserts.size,
175-
"upsertedIds" to result.upserts.map { it.id }))
170+
"upsertedIds" to result.upserts.map { it.id })
176171
}
177172
}
178173
}
@@ -218,9 +213,8 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
218213
getDatabase(database, dbOptions).flatMap { db ->
219214
convert(DeleteOptions(), deleteConverters, deleteDefaultConverter, options).map { deleteOptions ->
220215
val result = db.getCollection(collection).deleteMany(filter, deleteOptions)
221-
context.toJs(mapOf(
222-
"result" to mapOf("ok" to result.wasAcknowledged()),
223-
"deletedCount" to result.deletedCount))
216+
mapOf("result" to mapOf("ok" to result.wasAcknowledged()),
217+
"deletedCount" to result.deletedCount)
224218
}
225219
}
226220
}
@@ -233,9 +227,8 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
233227
getDatabase(database, dbOptions).flatMap { db ->
234228
convert(DeleteOptions(), deleteConverters, deleteDefaultConverter, options).map { deleteOptions ->
235229
val result = db.getCollection(collection).deleteOne(filter, deleteOptions)
236-
context.toJs(mapOf(
237-
"result" to mapOf("ok" to result.wasAcknowledged()),
238-
"deletedCount" to result.deletedCount))
230+
mapOf("result" to mapOf("ok" to result.wasAcknowledged()),
231+
"deletedCount" to result.deletedCount)
239232
}
240233
}
241234
}
@@ -247,7 +240,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
247240
getDatabase(database, null).flatMap { db ->
248241
convert(FindOneAndDeleteOptions(), findOneAndDeleteConverters, findOneAndDeleteDefaultConverter, options).map { options ->
249242
val res = db.getCollection(collection).findOneAndDelete(filter, options)
250-
context.toJs(mapOf("value" to res))
243+
mapOf("value" to res)
251244
}
252245
}
253246
}
@@ -261,7 +254,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
261254
convert(FindOneAndReplaceOptions(), findOneAndReplaceOptionsConverters, findOneAndReplaceOptionsDefaultConverters, options)
262255
.map { options ->
263256
val res = db.getCollection(collection).findOneAndReplace(filter, replacement, options)
264-
context.toJs(mapOf("value" to res))
257+
mapOf("value" to res)
265258
}
266259
}
267260
}
@@ -274,7 +267,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
274267
getDatabase(database, null).flatMap { db ->
275268
convert(FindOneAndUpdateOptions(), findOneAndUpdateConverters, findOneAndUpdateDefaultConverter, options).map { options ->
276269
val res = db.getCollection(collection).findOneAndUpdate(filter, update, options)
277-
context.toJs(mapOf("value" to res))
270+
mapOf("value" to res)
278271
}
279272
}
280273

@@ -287,9 +280,8 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
287280
if (docs == null || docs.any { it !is Document }) return@promise Left(IllegalArgumentException("docs must be a list of objects"))
288281
getDatabase(database, dbOptions).map { db ->
289282
db.getCollection(collection).insertMany(docs.filterIsInstance<Document>())
290-
context.toJs(mapOf(
291-
"result" to mapOf("ok" to true),
292-
"insertedIds" to emptyList<String>()))
283+
mapOf("result" to mapOf("ok" to true),
284+
"insertedIds" to emptyList<String>())
293285
}
294286
}
295287

@@ -392,7 +384,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
392384

393385
@HostAccess.Export
394386
override fun listDatabases(database: String): Value = promise {
395-
Right(context.toJs(mapOf("databases" to client.listDatabases())))
387+
Right(mapOf("databases" to client.listDatabases()))
396388
}
397389

398390
@HostAccess.Export
@@ -407,7 +399,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
407399
@HostAccess.Export
408400
override fun getIndexes(database: String, collection: String): Value = promise {
409401
getDatabase(database, null).map { db ->
410-
context.toJs(db.getCollection(collection).listIndexes())
402+
db.getCollection(collection).listIndexes()
411403
}
412404
}
413405

@@ -417,26 +409,26 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
417409
getDatabase(database, null).map { db ->
418410
val list = db.listCollections()
419411
if (filter != null) list.filter(filter)
420-
context.toJs(list)
412+
list
421413
}
422414
}
423415

424416
@HostAccess.Export
425417
override fun stats(database: String, collection: String, options: Value?): Value = promise<Any?> {
426418
getDatabase(database, null).map { db ->
427-
context.toJs(db.runCommand(Document("collStats", collection)))
419+
db.runCommand(Document("collStats", collection))
428420
}
429421
}
430422

431423
@HostAccess.Export
432-
override fun remove(database: String, collection: String, query: Value, options: Value?, dbOptions: Value?): Value {
424+
override fun remove(database: String, collection: String, query: Value, options: Value?, dbOptions: Value?): Value = promise {
433425
val query = toDocument(query, "query")
434426
val dbOptions = toDocument(dbOptions, "dbOptions")
435-
val promise = getDatabase(database, dbOptions).map { db ->
427+
getDatabase(database, dbOptions).map { db ->
436428
val result = db.getCollection(collection).deleteMany(query)
437-
DeleteResult(result.wasAcknowledged(), result.deletedCount)
429+
mapOf("acknowledged" to result.wasAcknowledged(),
430+
"deletedCount" to result.deletedCount)
438431
}
439-
return context.toJsPromise(promise)
440432
}
441433

442434
@HostAccess.Export
@@ -454,7 +446,7 @@ internal class JavaServiceProvider(private val client: MongoClient, private val
454446
}
455447
val indexes = unwrap(convertedIndexes)
456448
indexes.map { indexes ->
457-
context.toJs(db.getCollection(collection).createIndexes(indexes))
449+
db.getCollection(collection).createIndexes(indexes)
458450
}
459451
}
460452
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
null
21
{ "acknowledged": true, "insertedId": "UNKNOWN" }
32
true
43
[ { "_id": <ObjectID>, "a": 1, "objectId": <ObjectID>, "maxKey": { "$maxKey" : 1}, "minKey": { "$minKey" : 1}, "binData": { "$binary" : "MTIzNA==" , "$type" : 16}, "date": { "$date" : 1355875200000}, "isoDate": { "$date" : 1355875200000}, "numberInt": 24, "timestamp": Timestamp{value=429496729600, seconds=100, inc=0} } ]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
NullResult: null
1+
VoidResult:

0 commit comments

Comments
 (0)