Skip to content

Commit 39ea906

Browse files
committed
Merge branch '33-js-output' into dev
2 parents 7cc16c2 + 97c8695 commit 39ea906

File tree

8 files changed

+1266
-0
lines changed

8 files changed

+1266
-0
lines changed

cmd/objectbox-generator/objectbox-generator.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
cgenerator "github.com/objectbox/objectbox-generator/v4/internal/generator/c"
3636
"github.com/objectbox/objectbox-generator/v4/internal/generator/flatbuffersc"
3737
gogenerator "github.com/objectbox/objectbox-generator/v4/internal/generator/go"
38+
jsgenerator "github.com/objectbox/objectbox-generator/v4/internal/generator/js"
3839
)
3940

4041
func main() {
@@ -91,6 +92,7 @@ func (cmd *command) ConfigureFlags() {
9192
cmd.langs["c"] = flag.Bool("c", false, "generate plain C code")
9293
cmd.langs["cpp"] = flag.Bool("cpp", false, "generate C++ code (at least C++14)")
9394
cmd.langs["cpp11"] = flag.Bool("cpp11", false, "generate C++11 code")
95+
cmd.langs["js"] = flag.Bool("js", false, "generate JS code")
9496
cmd.langs["go"] = flag.Bool("go", false, "generate Go code")
9597

9698
// for c++ generator
@@ -139,6 +141,12 @@ func (cmd *command) ParseFlags(remainingPosArgs *[]string, options *generator.Op
139141
EmptyStringAsNull: *cmd.empty_string_as_null,
140142
NaNAsNull: *cmd.nan_as_null,
141143
}
144+
case "js":
145+
options.CodeGenerator = &jsgenerator.JSGenerator{
146+
Optional: *cmd.optional,
147+
EmptyStringAsNull: *cmd.empty_string_as_null,
148+
NaNAsNull: *cmd.nan_as_null,
149+
}
142150
default:
143151
return errors.New("you must specify an output language")
144152
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* ObjectBox Generator - a build time tool for ObjectBox
3+
* Copyright (C) 2018-2024 ObjectBox Ltd. All rights reserved.
4+
* https://objectbox.io
5+
*
6+
* This file is part of ObjectBox Generator.
7+
*
8+
* ObjectBox Generator is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
* ObjectBox Generator is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with ObjectBox Generator. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package jsgenerator
22+
23+
import (
24+
"bufio"
25+
"bytes"
26+
"fmt"
27+
"path/filepath"
28+
"strings"
29+
30+
"github.com/objectbox/objectbox-generator/v4/internal/generator"
31+
"github.com/objectbox/objectbox-generator/v4/internal/generator/flatbuffersc"
32+
"github.com/objectbox/objectbox-generator/v4/internal/generator/js/templates"
33+
"github.com/objectbox/objectbox-generator/v4/internal/generator/model"
34+
)
35+
36+
// JS generator, given a .fbs and an optional *model.json file, is responsible for generating:
37+
// - objectbox-model.js
38+
// - sche
39+
type JSGenerator struct {
40+
Optional string // std::optional, std::unique_ptr, std::shared_ptr
41+
EmptyStringAsNull bool
42+
NaNAsNull bool
43+
}
44+
45+
// Return the names of the generated JS binding file (only one!) for the given entity file.
46+
// For example: given a schema.fbs file, outputs schema.obx.fbs.
47+
func (gen *JSGenerator) BindingFiles(forFile string, options generator.Options) []string {
48+
49+
if len(options.OutPath) > 0 {
50+
forFile = filepath.Join(options.OutPath, filepath.Base(forFile))
51+
}
52+
var extension = filepath.Ext(forFile)
53+
var base = forFile[0 : len(forFile)-len(extension)]
54+
return []string{base + ".obx.js"}
55+
}
56+
57+
// Return the model filename for the given model JSON file.
58+
func (gen *JSGenerator) ModelFile(forFile string, options generator.Options) string {
59+
if len(options.OutPath) > 0 {
60+
forFile = filepath.Join(options.OutPath, filepath.Base(forFile))
61+
}
62+
var extension = filepath.Ext(forFile)
63+
var fileStem = forFile[0 : len(forFile)-len(extension)]
64+
return fileStem + ".js"
65+
}
66+
67+
func (JSGenerator) IsGeneratedFile(file string) bool {
68+
var name = filepath.Base(file)
69+
return name == "objectbox-model.js" || name == "schema.obx.js"
70+
}
71+
72+
func (JSGenerator) IsSourceFile(file string) bool {
73+
return strings.HasSuffix(file, ".fbs")
74+
}
75+
76+
func (gen *JSGenerator) ParseSource(sourceFile string) (*model.ModelInfo, error) {
77+
schemaReflection, err := flatbuffersc.ParseSchemaFile(sourceFile)
78+
if err != nil {
79+
return nil, err // already includes file name so no more context should be necessary
80+
}
81+
82+
reader := fbSchemaReader{model: &model.ModelInfo{}, optional: gen.Optional}
83+
if err = reader.read(schemaReflection); err != nil {
84+
return nil, fmt.Errorf("error generating model from schema %s: %s", sourceFile, err)
85+
}
86+
87+
return reader.model, nil
88+
}
89+
90+
// Generate the schema.obx.js file, given the merged model info
91+
func (gen *JSGenerator) WriteBindingFiles(sourceFile string, options generator.Options, mergedModel *model.ModelInfo) error {
92+
var err, err2 error
93+
94+
var bindingFile = gen.BindingFiles(sourceFile, options)[0]
95+
96+
// First generate the binding source
97+
var bindingSource []byte
98+
if bindingSource, err = gen.generateBindingFile(bindingFile, mergedModel); err != nil {
99+
return fmt.Errorf("can't generate binding file %s: %s", sourceFile, err)
100+
}
101+
102+
if formattedSource, err := format(bindingSource); err != nil {
103+
// We just store error but still write the file so that we can check it manually
104+
err2 = fmt.Errorf("failed to format generated binding file %s: %s", bindingFile, err)
105+
} else {
106+
bindingSource = formattedSource
107+
}
108+
109+
if err = generator.WriteFile(bindingFile, bindingSource, sourceFile); err != nil {
110+
return fmt.Errorf("can't write binding file %s: %s", sourceFile, err)
111+
} else if err2 != nil {
112+
// Now when the binding has been written (for debugging purposes), we can return the error
113+
return err2
114+
}
115+
116+
return nil
117+
}
118+
119+
func (gen *JSGenerator) generateBindingFile(bindingFile string, modelInfo *model.ModelInfo) (data []byte, err error) {
120+
var b bytes.Buffer
121+
writer := bufio.NewWriter(&b)
122+
123+
var replaceSpecialChars = strings.NewReplacer("-", "_", ".", "_")
124+
var fileIdentifier = strings.ToLower(filepath.Base(bindingFile))
125+
fileIdentifier = replaceSpecialChars.Replace(fileIdentifier)
126+
127+
// Arguments for the template
128+
type TplArgs struct {
129+
Model *model.ModelInfo
130+
GeneratorVersion int
131+
FileIdentifier string
132+
Optional string
133+
LangVersion int
134+
EmptyStringAsNull bool
135+
NaNAsNull bool
136+
}
137+
var tplArgs TplArgs
138+
tplArgs.Model = modelInfo
139+
tplArgs.GeneratorVersion = generator.VersionId
140+
tplArgs.FileIdentifier = fileIdentifier
141+
tplArgs.Optional = gen.Optional
142+
tplArgs.EmptyStringAsNull = gen.EmptyStringAsNull
143+
tplArgs.NaNAsNull = gen.NaNAsNull
144+
145+
var tpl = templates.JsBindingTemplate
146+
147+
if err = tpl.Execute(writer, tplArgs); err != nil {
148+
return nil, fmt.Errorf("template execution failed: %s", err)
149+
}
150+
151+
if err = writer.Flush(); err != nil {
152+
return nil, fmt.Errorf("failed to flush buffer: %s", err)
153+
}
154+
155+
return b.Bytes(), nil
156+
}
157+
158+
// Generate the objectbox-model.js, given the merged model info
159+
func (gen *JSGenerator) WriteModelBindingFile(options generator.Options, mergedModel *model.ModelInfo) error {
160+
var err, err2 error
161+
162+
var modelFile = gen.ModelFile(options.ModelInfoFile, options)
163+
var modelSource []byte
164+
165+
if modelSource, err = generateModelFile(mergedModel); err != nil {
166+
return fmt.Errorf("can't generate model file %s: %s", modelFile, err)
167+
}
168+
169+
if formattedSource, err := format(modelSource); err != nil {
170+
// we just store error but still writ the file so that we can check it manually
171+
err2 = fmt.Errorf("failed to format generated model file %s: %s", modelFile, err)
172+
} else {
173+
modelSource = formattedSource
174+
}
175+
176+
if err = generator.WriteFile(modelFile, modelSource, options.ModelInfoFile); err != nil {
177+
return fmt.Errorf("can't write model file %s: %s", modelFile, err)
178+
} else if err2 != nil {
179+
// now when the model has been written (for debugging purposes), we can return the error
180+
return err2
181+
}
182+
183+
return nil
184+
}
185+
186+
func generateModelFile(m *model.ModelInfo) (data []byte, err error) {
187+
var b bytes.Buffer
188+
writer := bufio.NewWriter(&b)
189+
190+
var tplArguments = struct {
191+
Model *model.ModelInfo
192+
GeneratorVersion int
193+
}{m, generator.VersionId}
194+
195+
if err = templates.JsModelTemplate.Execute(writer, tplArguments); err != nil {
196+
return nil, fmt.Errorf("template execution failed: %s", err)
197+
}
198+
199+
if err = writer.Flush(); err != nil {
200+
return nil, fmt.Errorf("failed to flush buffer: %s", err)
201+
}
202+
203+
return b.Bytes(), nil
204+
}
205+
206+
func removeEmptyLines(source []byte) []byte {
207+
// Split the source into lines
208+
lines := bytes.Split(source, []byte("\n"))
209+
210+
// Filter out empty or whitespace-only lines
211+
var filteredLines [][]byte
212+
for _, line := range lines {
213+
if strings.TrimSpace(string(line)) != "" {
214+
filteredLines = append(filteredLines, line)
215+
}
216+
}
217+
218+
// Join the filtered lines back together
219+
return bytes.Join(filteredLines, []byte("\n"))
220+
}
221+
222+
func format(source []byte) ([]byte, error) {
223+
// NOTE we could do JS source formatting here
224+
225+
// Replace tabs with spaces
226+
formatted := bytes.ReplaceAll(source, []byte("\t"), []byte(" "))
227+
//formatted = removeEmptyLines(formatted)
228+
229+
return formatted, nil
230+
}

internal/generator/js/maps.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* ObjectBox Generator - a build time tool for ObjectBox
3+
* Copyright (C) 2018-2024 ObjectBox Ltd. All rights reserved.
4+
* https://objectbox.io
5+
*
6+
* This file is part of ObjectBox Generator.
7+
*
8+
* ObjectBox Generator is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
* ObjectBox Generator is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Affero General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Affero General Public License
18+
* along with ObjectBox Generator. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package jsgenerator
22+
23+
import (
24+
flatbuffers "github.com/google/flatbuffers/go"
25+
26+
"github.com/objectbox/objectbox-generator/v4/internal/generator/flatbuffersc/reflection"
27+
"github.com/objectbox/objectbox-generator/v4/internal/generator/model"
28+
)
29+
30+
var fbsTypeToObxType = map[reflection.BaseType]model.PropertyType{
31+
reflection.BaseTypeNone: 0,
32+
reflection.BaseTypeUType: 0,
33+
reflection.BaseTypeBool: model.PropertyTypeBool,
34+
reflection.BaseTypeByte: model.PropertyTypeByte,
35+
reflection.BaseTypeUByte: model.PropertyTypeByte,
36+
reflection.BaseTypeShort: model.PropertyTypeShort,
37+
reflection.BaseTypeUShort: model.PropertyTypeShort,
38+
reflection.BaseTypeInt: model.PropertyTypeInt,
39+
reflection.BaseTypeUInt: model.PropertyTypeInt,
40+
reflection.BaseTypeLong: model.PropertyTypeLong,
41+
reflection.BaseTypeULong: model.PropertyTypeLong,
42+
reflection.BaseTypeFloat: model.PropertyTypeFloat,
43+
reflection.BaseTypeDouble: model.PropertyTypeDouble,
44+
reflection.BaseTypeString: model.PropertyTypeString,
45+
reflection.BaseTypeVector: 0, // handled in schema-reader
46+
reflection.BaseTypeObj: 0, // not supported
47+
reflection.BaseTypeUnion: 0, // not supported
48+
reflection.BaseTypeArray: 0, // not supported
49+
}
50+
51+
var fbsTypeToObxFlag = map[reflection.BaseType]model.PropertyFlags{
52+
reflection.BaseTypeUByte: model.PropertyFlagUnsigned,
53+
reflection.BaseTypeUShort: model.PropertyFlagUnsigned,
54+
reflection.BaseTypeUInt: model.PropertyFlagUnsigned,
55+
reflection.BaseTypeULong: model.PropertyFlagUnsigned,
56+
}
57+
58+
var fbsTypeToCppType = map[reflection.BaseType]string{
59+
reflection.BaseTypeNone: "",
60+
reflection.BaseTypeUType: "",
61+
reflection.BaseTypeBool: "bool",
62+
reflection.BaseTypeByte: "int8_t",
63+
reflection.BaseTypeUByte: "uint8_t",
64+
reflection.BaseTypeShort: "int16_t",
65+
reflection.BaseTypeUShort: "uint16_t",
66+
reflection.BaseTypeInt: "int32_t",
67+
reflection.BaseTypeUInt: "uint32_t",
68+
reflection.BaseTypeLong: "int64_t",
69+
reflection.BaseTypeULong: "uint64_t",
70+
reflection.BaseTypeFloat: "float",
71+
reflection.BaseTypeDouble: "double",
72+
reflection.BaseTypeString: "std::string",
73+
reflection.BaseTypeVector: "std::vector", // Note: additional handling in fbsField
74+
reflection.BaseTypeObj: "",
75+
reflection.BaseTypeUnion: "",
76+
reflection.BaseTypeArray: "",
77+
}
78+
79+
var fbsTypeSize = map[reflection.BaseType]uint8{
80+
reflection.BaseTypeNone: 0,
81+
reflection.BaseTypeUType: 0,
82+
reflection.BaseTypeBool: flatbuffers.SizeBool,
83+
reflection.BaseTypeByte: flatbuffers.SizeByte,
84+
reflection.BaseTypeUByte: flatbuffers.SizeByte,
85+
reflection.BaseTypeShort: flatbuffers.SizeInt16,
86+
reflection.BaseTypeUShort: flatbuffers.SizeUint16,
87+
reflection.BaseTypeInt: flatbuffers.SizeInt32,
88+
reflection.BaseTypeUInt: flatbuffers.SizeUint32,
89+
reflection.BaseTypeLong: flatbuffers.SizeInt64,
90+
reflection.BaseTypeULong: flatbuffers.SizeUint64,
91+
reflection.BaseTypeFloat: flatbuffers.SizeFloat32,
92+
reflection.BaseTypeDouble: flatbuffers.SizeFloat64,
93+
reflection.BaseTypeString: flatbuffers.SizeUOffsetT,
94+
reflection.BaseTypeVector: flatbuffers.SizeUOffsetT,
95+
reflection.BaseTypeObj: 0,
96+
reflection.BaseTypeUnion: 0,
97+
reflection.BaseTypeArray: 0,
98+
}
99+
100+
var fbsTypeToFlatccFnPrefix = map[reflection.BaseType]string{
101+
reflection.BaseTypeNone: "",
102+
reflection.BaseTypeUType: "",
103+
reflection.BaseTypeBool: "flatbuffers_bool",
104+
reflection.BaseTypeByte: "flatbuffers_int8",
105+
reflection.BaseTypeUByte: "flatbuffers_uint8",
106+
reflection.BaseTypeShort: "flatbuffers_int16",
107+
reflection.BaseTypeUShort: "flatbuffers_uint16",
108+
reflection.BaseTypeInt: "flatbuffers_int32",
109+
reflection.BaseTypeUInt: "flatbuffers_uint32",
110+
reflection.BaseTypeLong: "flatbuffers_int64",
111+
reflection.BaseTypeULong: "flatbuffers_uint64",
112+
reflection.BaseTypeFloat: "flatbuffers_float",
113+
reflection.BaseTypeDouble: "flatbuffers_double",
114+
reflection.BaseTypeString: "__flatbuffers_soffset",
115+
reflection.BaseTypeVector: "__flatbuffers_soffset",
116+
reflection.BaseTypeObj: "",
117+
reflection.BaseTypeUnion: "",
118+
reflection.BaseTypeArray: "",
119+
}

0 commit comments

Comments
 (0)