Skip to content

Commit

Permalink
Experiments
Browse files Browse the repository at this point in the history
  • Loading branch information
rorbech committed Aug 13, 2024
1 parent 06aacec commit 58b168b
Show file tree
Hide file tree
Showing 37 changed files with 1,303 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ expect object RealmInterop {
fun realm_config_set_automatic_backlink_handling(config: RealmConfigurationPointer, enabled: Boolean)
fun realm_config_set_data_initialization_function(config: RealmConfigurationPointer, callback: DataInitializationCallback)
fun realm_config_set_in_memory(config: RealmConfigurationPointer, inMemory: Boolean)
fun realm_config_set_relaxed_schema(config: RealmConfigurationPointer, relaxedSchema: Boolean)
fun realm_schema_validate(schema: RealmSchemaPointer, mode: SchemaValidationMode): Boolean

fun realm_create_scheduler(): RealmSchedulerPointer
Expand Down Expand Up @@ -299,15 +300,26 @@ expect object RealmInterop {
fun realm_get_col_key(realm: RealmPointer, classKey: ClassKey, col: String): PropertyKey

fun MemAllocator.realm_get_value(obj: RealmObjectPointer, key: PropertyKey): RealmValue
fun MemAllocator.realm_get_value_by_name(obj: RealmObjectPointer, name: String): RealmValue
fun realm_set_value(
obj: RealmObjectPointer,
key: PropertyKey,
value: RealmValue,
isDefault: Boolean
)
fun realm_set_value_by_name(
obj: RealmObjectPointer,
name: String,
value: RealmValue,
)
fun realm_has_property(obj: RealmObjectPointer, name: String): Boolean
fun realm_get_additional_properties(obj: RealmObjectPointer): List<String>
fun realm_erase_property(obj: RealmObjectPointer, key: String): Boolean
fun realm_set_embedded(obj: RealmObjectPointer, key: PropertyKey): RealmObjectPointer
fun realm_set_list(obj: RealmObjectPointer, key: PropertyKey): RealmListPointer
fun realm_set_list_by_name(obj: RealmObjectPointer, propertyName: String): RealmListPointer
fun realm_set_dictionary(obj: RealmObjectPointer, key: PropertyKey): RealmMapPointer
fun realm_set_dictionary_by_name(obj: RealmObjectPointer, propertyName: String): RealmMapPointer
fun realm_object_add_int(obj: RealmObjectPointer, key: PropertyKey, value: Long)
fun <T> realm_object_get_parent(
obj: RealmObjectPointer,
Expand All @@ -316,6 +328,7 @@ expect object RealmInterop {

// list
fun realm_get_list(obj: RealmObjectPointer, key: PropertyKey): RealmListPointer
fun realm_get_list_by_name(obj: RealmObjectPointer, propertyName: String): RealmListPointer
fun realm_get_backlinks(obj: RealmObjectPointer, sourceClassKey: ClassKey, sourcePropertyKey: PropertyKey): RealmResultsPointer
fun realm_list_size(list: RealmListPointer): Long
fun MemAllocator.realm_list_get(list: RealmListPointer, index: Long): RealmValue
Expand Down Expand Up @@ -354,6 +367,7 @@ expect object RealmInterop {

// dictionary
fun realm_get_dictionary(obj: RealmObjectPointer, key: PropertyKey): RealmMapPointer
fun realm_get_dictionary_by_name(obj: RealmObjectPointer, propertyName: String): RealmMapPointer
fun realm_dictionary_clear(dictionary: RealmMapPointer)
fun realm_dictionary_size(dictionary: RealmMapPointer): Long
fun realm_dictionary_to_results(dictionary: RealmMapPointer): RealmResultsPointer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ expect enum class WebsocketErrorCode : CodeDescription {
RLM_ERR_WEBSOCKET_UNAUTHORIZED,
RLM_ERR_WEBSOCKET_FORBIDDEN,
RLM_ERR_WEBSOCKET_MOVEDPERMANENTLY,
RLM_ERR_WEBSOCKET_CLIENT_TOO_OLD,
RLM_ERR_WEBSOCKET_CLIENT_TOO_NEW,
RLM_ERR_WEBSOCKET_PROTOCOL_MISMATCH,
RLM_ERR_WEBSOCKET_RESOLVE_FAILED,
RLM_ERR_WEBSOCKET_CONNECTION_FAILED,
RLM_ERR_WEBSOCKET_READ_ERROR,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ actual object RealmInterop {
realmc.realm_config_set_in_memory(config.cptr(), inMemory)
}

actual fun realm_config_set_relaxed_schema(config: RealmConfigurationPointer, relaxedSchema: Boolean) {
realmc.realm_config_set_flexible_schema(config.cptr(), relaxedSchema)
}

actual fun realm_create_scheduler(): RealmSchedulerPointer =
LongPointerWrapper(realmc.realm_create_generic_scheduler())

Expand Down Expand Up @@ -489,6 +493,15 @@ actual object RealmInterop {
return RealmValue(struct)
}

actual fun MemAllocator.realm_get_value_by_name(
obj: RealmObjectPointer,
name: String,
): RealmValue {
val struct = allocRealmValueT()
realmc.realm_get_value_by_name((obj as LongPointerWrapper).ptr, name, struct)
return RealmValue(struct)
}

actual fun realm_set_value(
obj: RealmObjectPointer,
key: PropertyKey,
Expand All @@ -498,6 +511,29 @@ actual object RealmInterop {
realmc.realm_set_value(obj.cptr(), key.key, value.value, isDefault)
}

actual fun realm_set_value_by_name(
obj: RealmObjectPointer,
name: String,
value: RealmValue,
) {
realmc.realm_set_value_by_name(obj.cptr(), name, value.value)
}

actual fun realm_has_property(obj: RealmObjectPointer, name: String): Boolean {
val found = BooleanArray(1)
realmc.realm_has_property(obj.cptr(), name, found)
return found[0]
}

actual fun realm_get_additional_properties(obj: RealmObjectPointer): List<String> {
@Suppress("UNCHECKED_CAST")
val properties = realmc.realm_get_additional_properties_helper(obj.cptr()) as Array<String>

Check failure on line 530 in packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt

View workflow job for this annotation

GitHub Actions / static-analysis / ktlint

Unnecessary long whitespace (no-multi-spaces)
return properties.asList()
}
actual fun realm_erase_property(obj: RealmObjectPointer, key: String): Boolean {
return realmc.realm_erase_additional_property(obj.cptr(), key)
}

actual fun realm_set_embedded(obj: RealmObjectPointer, key: PropertyKey): RealmObjectPointer {
return LongPointerWrapper(realmc.realm_set_embedded(obj.cptr(), key.key))
}
Expand All @@ -506,10 +542,18 @@ actual object RealmInterop {
realmc.realm_set_list(obj.cptr(), key.key)
return realm_get_list(obj, key)
}
actual fun realm_set_list_by_name(obj: RealmObjectPointer, propertyName: String): RealmListPointer {
realmc.realm_set_list_by_name(obj.cptr(), propertyName)
return realm_get_list_by_name(obj, propertyName)
}
actual fun realm_set_dictionary(obj: RealmObjectPointer, key: PropertyKey): RealmMapPointer {
realmc.realm_set_dictionary(obj.cptr(), key.key)
return realm_get_dictionary(obj, key)
}
actual fun realm_set_dictionary_by_name(obj: RealmObjectPointer, propertyName: String): RealmMapPointer {
realmc.realm_set_dictionary_by_name(obj.cptr(), propertyName)
return realm_get_dictionary_by_name(obj, propertyName)
}

actual fun realm_object_add_int(obj: RealmObjectPointer, key: PropertyKey, value: Long) {
realmc.realm_object_add_int(obj.cptr(), key.key, value)
Expand Down Expand Up @@ -542,6 +586,14 @@ actual object RealmInterop {
)
)
}
actual fun realm_get_list_by_name(obj: RealmObjectPointer, propertyName: String): RealmListPointer {
return LongPointerWrapper(
realmc.realm_get_list_by_name(
(obj as LongPointerWrapper).ptr,
propertyName
)
)
}

actual fun realm_get_backlinks(obj: RealmObjectPointer, sourceClassKey: ClassKey, sourcePropertyKey: PropertyKey): RealmResultsPointer {
return LongPointerWrapper(
Expand Down Expand Up @@ -731,6 +783,14 @@ actual object RealmInterop {
return LongPointerWrapper(ptr)
}

actual fun realm_get_dictionary_by_name(
obj: RealmObjectPointer,
propertyName: String
): RealmMapPointer {
val ptr = realmc.realm_get_dictionary_by_name(obj.cptr(), propertyName)
return LongPointerWrapper(ptr)
}

actual fun realm_dictionary_clear(dictionary: RealmMapPointer) {
realmc.realm_dictionary_clear(dictionary.cptr())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,6 @@ actual enum class WebsocketErrorCode(
RLM_ERR_WEBSOCKET_UNAUTHORIZED("Unauthorized", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_UNAUTHORIZED),
RLM_ERR_WEBSOCKET_FORBIDDEN("Forbidden", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_FORBIDDEN),
RLM_ERR_WEBSOCKET_MOVEDPERMANENTLY("MovedPermanently", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_MOVEDPERMANENTLY),
RLM_ERR_WEBSOCKET_CLIENT_TOO_OLD("ClientTooOld", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_CLIENT_TOO_OLD),
RLM_ERR_WEBSOCKET_CLIENT_TOO_NEW("ClientTooNew", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_CLIENT_TOO_NEW),
RLM_ERR_WEBSOCKET_PROTOCOL_MISMATCH("ProtocolMismatch", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_PROTOCOL_MISMATCH),

RLM_ERR_WEBSOCKET_RESOLVE_FAILED("ResolveFailed", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_RESOLVE_FAILED),
RLM_ERR_WEBSOCKET_CONNECTION_FAILED("ConnectionFailed", realm_web_socket_errno_e.RLM_ERR_WEBSOCKET_CONNECTION_FAILED),
Expand Down
2 changes: 1 addition & 1 deletion packages/external/core
Submodule core updated 180 files
2 changes: 1 addition & 1 deletion packages/jni-swig-stub/realm.i
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ import static io.realm.kotlin.internal.interop.realm_errno_e.*;
bool* erased, bool* out_erased, bool* did_refresh, bool* did_run,
bool* found, bool* out_collection_was_cleared, bool* did_compact,
bool* collection_was_cleared, bool* out_collection_was_deleted,
bool* out_was_deleted};
bool* out_was_deleted, bool* out_has_property };

// uint64_t output parameter for realm_get_num_versions
%apply int64_t* OUTPUT { uint64_t* out_versions_count };
Expand Down
22 changes: 22 additions & 0 deletions packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1411,3 +1411,25 @@ jobjectArray realm_get_log_category_names() {

return array;
}

jobjectArray realm_get_additional_properties_helper(realm_object_t* obj) {
JNIEnv* env = get_env(true);

size_t count = 0;
realm_get_additional_properties(obj, nullptr, 0xffffff, &count);

const char** properties = new const char*[count];
realm_get_additional_properties(obj, properties, count, &count);
// FIXME Guard count != count

auto array = env->NewObjectArray(count, JavaClassGlobalDef::java_lang_string(), nullptr);

for(size_t i = 0; i < count; i++) {
jstring string = env->NewStringUTF(properties[i]);
env->SetObjectArrayElement(array, i, string);
}

delete[] properties;

return array;
}
1 change: 1 addition & 0 deletions packages/jni-swig-stub/src/main/jni/realm_api_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,6 @@ bool realm_sync_websocket_message(int64_t observer_ptr, jbyteArray data, size_t
void realm_sync_websocket_closed(int64_t observer_ptr, bool was_clean, int error_code, const char* reason);

jobjectArray realm_get_log_category_names();
jobjectArray realm_get_additional_properties_helper(realm_object_t* obj);

#endif //TEST_REALM_API_HELPERS_H
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public interface BaseRealm : Versioned {
* @return the schema of the realm.
*/
public fun schema(): RealmSchema
// public fun schema(fullSchema: Boolean = false): RealmSchema

/**
* Returns the schema version of the realm.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ public interface Configuration {
*/
public val initialRealmFileConfiguration: InitialRealmFileConfiguration?

// FIXME DOCS
public val relaxedSchema: Boolean

/**
* Base class for configuration builders that holds properties available to both
* [RealmConfiguration] and [SyncConfiguration].
Expand Down Expand Up @@ -209,6 +212,7 @@ public interface Configuration {
protected var initialDataCallback: InitialDataCallback? = null
protected var inMemory: Boolean = false
protected var initialRealmFileConfiguration: InitialRealmFileConfiguration? = null
protected var relaxedSchema: Boolean = false

/**
* Sets the filename of the realm file.
Expand Down Expand Up @@ -399,6 +403,12 @@ public interface Configuration {
return this as S
}

// FIXME Docs
public fun relaxedSchema(relaxedSchema: Boolean): S {
this.relaxedSchema = relaxedSchema
return this as S
}

protected fun validateEncryptionKey(encryptionKey: ByteArray): ByteArray {
if (encryptionKey.size != Realm.ENCRYPTION_KEY_LENGTH) {
throw IllegalArgumentException("The provided key must be ${Realm.ENCRYPTION_KEY_LENGTH} bytes. The provided key was ${encryptionKey.size} bytes.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ public interface RealmConfiguration : Configuration {
initialDataCallback,
inMemory,
initialRealmFileConfiguration,
relaxedSchema,
realmLogger
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Realm Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.realm.kotlin.annotations

@MustBeDocumented
@Target(
AnnotationTarget.FUNCTION,
)
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
public annotation class DynamicAPI
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2024 Realm Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.realm.kotlin.dynamic.getgeneric

import io.realm.kotlin.annotations.DynamicAPI
import io.realm.kotlin.internal.RealmObjectHelper
import io.realm.kotlin.internal.runIfManagedOrThrow
import io.realm.kotlin.types.BaseRealmObject
import kotlin.reflect.KType
import kotlin.reflect.typeOf

// Proposal 1 of generic/dynamic API
// This seems like the most promising
@DynamicAPI
public inline operator fun <reified T> BaseRealmObject.get(propertyName: String): T {
return get(propertyName, typeOf<T>())
}

@DynamicAPI
public operator fun <T> BaseRealmObject.set(name: String, value: T) {
return this.runIfManagedOrThrow {
RealmObjectHelper.setValueByName(this, name, value)
}
}

@PublishedApi
internal fun <T> BaseRealmObject.get(propertyName: String, type: KType): T {
return this.runIfManagedOrThrow {
RealmObjectHelper.dynamicGetFromKType(
obj = this,
propertyName = propertyName,
type = type
)
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2024 Realm Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.realm.kotlin.dynamic.getinterface

import io.realm.kotlin.internal.RealmObjectHelper
import io.realm.kotlin.internal.runIfManagedOrThrow
import io.realm.kotlin.types.BaseRealmObject
import kotlin.reflect.KType
import kotlin.reflect.typeOf

// Proposal 2 of generic/dynamic API
// Hiding things a bit by only exposing it on a specific type. This is a bit cumbersome as you
// cannot use a single entry point to get into the dynamic domain (requires to use .relaxed
// everywhere)
public interface RelaxedRealmObject

// FIXME Naming
// - Extras?
public val BaseRealmObject.relaxed: RelaxedRealmObject
get() = this as RelaxedRealmObject


public inline operator fun <reified T> RelaxedRealmObject.get(propertyName: String): T {
return get(propertyName, typeOf<T>())
}

public operator fun <T> RelaxedRealmObject.set(name: String, value: T) {
return (this as BaseRealmObject).runIfManagedOrThrow {
RealmObjectHelper.setValueByName(this, name, value)
}
}

@PublishedApi
internal fun <T> RelaxedRealmObject.get(propertyName: String, type: KType): T {
return (this as BaseRealmObject).runIfManagedOrThrow {
RealmObjectHelper.dynamicGetFromKType(
obj = this,
propertyName = propertyName,
type = type
)
}
}
Loading

0 comments on commit 58b168b

Please sign in to comment.