blob: 2486e93f3f3e6dcb1fa908412e22e49848e59bab [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import { TypeInfo } from "../typeInfo";
import { CodecBuilder } from "./builder";
import { BaseSerializerGenerator } from "./serializer";
import { CodegenRegistry } from "./router";
import { Serializer, TypeId } from "../type";
import { Scope } from "./scope";
import { TypeMeta } from "../meta/TypeMeta";
import { ReadContext, WriteContext } from "../context";
export class AnyHelper {
static detectSerializer(readContext: ReadContext) {
const reader = readContext.reader;
const typeResolver = readContext.typeResolver;
const typeId = reader.readUint8();
let userTypeId = -1;
if (TypeId.needsUserTypeId(typeId) && typeId !== TypeId.COMPATIBLE_STRUCT) {
userTypeId = reader.readVarUint32Small7();
}
let serializer: Serializer | undefined;
function buildNamedTypeKey(ns: string, typeName: string) {
return `${ns}$${typeName}`;
}
function tryUpdateSerializer(serializer: Serializer | undefined | null, typeMeta: TypeMeta) {
if (!serializer) {
return readContext.genSerializerByTypeMetaRuntime(typeMeta);
}
const hash = serializer.getHash();
if (hash !== typeMeta.getHash()) {
return readContext.genSerializerByTypeMetaRuntime(typeMeta, serializer);
}
return serializer;
}
switch (typeId) {
case TypeId.COMPATIBLE_STRUCT:
{
const typeMeta = readContext.readTypeMeta();
serializer = typeResolver.getSerializerById(typeId, typeMeta.getUserTypeId());
serializer = tryUpdateSerializer(serializer, typeMeta);
}
break;
case TypeId.NAMED_ENUM:
case TypeId.NAMED_UNION:
if (readContext.isCompatible()) {
const typeMeta = readContext.readTypeMeta();
const ns = typeMeta.getNs();
const typeName = typeMeta.getTypeName();
serializer = typeResolver.getSerializerByName(buildNamedTypeKey(ns, typeName));
} else {
const ns = readContext.readNamespace();
const typeName = readContext.readTypeName();
serializer = typeResolver.getSerializerByName(buildNamedTypeKey(ns, typeName));
}
break;
case TypeId.NAMED_EXT:
if (readContext.isCompatible()) {
const typeMeta = readContext.readTypeMeta();
const ns = typeMeta.getNs();
const typeName = typeMeta.getTypeName();
serializer = typeResolver.getSerializerByName(buildNamedTypeKey(ns, typeName));
} else {
const ns = readContext.readNamespace();
const typeName = readContext.readTypeName();
serializer = typeResolver.getSerializerByName(buildNamedTypeKey(ns, typeName));
}
break;
case TypeId.NAMED_STRUCT:
case TypeId.NAMED_COMPATIBLE_STRUCT:
if (readContext.isCompatible() || typeId === TypeId.NAMED_COMPATIBLE_STRUCT) {
const typeMeta = readContext.readTypeMeta();
const ns = typeMeta.getNs();
const typeName = typeMeta.getTypeName();
const named = buildNamedTypeKey(ns, typeName);
const namedSerializer = typeResolver.getSerializerByName(named);
serializer = tryUpdateSerializer(namedSerializer, typeMeta);
} else {
const ns = readContext.readNamespace();
const typeName = readContext.readTypeName();
serializer = typeResolver.getSerializerByName(buildNamedTypeKey(ns, typeName));
}
break;
default:
serializer = typeResolver.getSerializerById(typeId, userTypeId);
break;
}
if (!serializer) {
throw new Error(`can't find implements of typeId: ${typeId}`);
}
return serializer;
}
static getSerializer(writeContext: WriteContext, v: any) {
if (v === null || v === undefined) {
throw new Error("can not guess the type of null or undefined");
}
const serializer = writeContext.typeResolver.getSerializerByData(v);
if (!serializer) {
throw new Error(`Failed to detect the Fory serializer from JavaScript type: ${typeof v}`);
}
writeContext.writer.reserve(serializer.fixedSize);
return serializer;
}
}
class AnySerializerGenerator extends BaseSerializerGenerator {
typeInfo: TypeInfo;
detectedSerializer: string;
writerSerializer: string;
constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
super(typeInfo, builder, scope);
this.typeInfo = typeInfo;
this.detectedSerializer = this.scope.declareVar("detectedSerializer", "null");
this.writerSerializer = this.scope.declareVar("writerSerializer", "null");
}
write(accessor: string): string {
return `
${this.writerSerializer}.write(${accessor});;
`;
}
writeTypeInfo(accessor: string): string {
return `
${this.writerSerializer} = ${this.builder.getExternal(AnyHelper.name)}.getSerializer(${this.builder.getWriteContextName()}, ${accessor});
${this.writerSerializer}.writeTypeInfo();
`;
}
readTypeInfo(): string {
return `
${this.detectedSerializer} = ${this.builder.getExternal(AnyHelper.name)}.detectSerializer(${this.builder.getReadContextName()});
`;
}
read(assignStmt: (v: string) => string, refState: string): string {
return assignStmt(`${this.detectedSerializer}.read(${refState});`);
}
getFixedSize(): number {
return 11;
}
}
CodegenRegistry.register(TypeId.UNKNOWN, AnySerializerGenerator);
CodegenRegistry.registerExternal(AnyHelper);