Browse Source

Merge pull request #872 from svaarala/add-rom-builtin-lightfunc-support

Add ROM built-in lightfunc conversion
pull/566/merge
Sami Vaarala 8 years ago
committed by GitHub
parent
commit
f07d8bba37
  1. 4
      RELEASES.rst
  2. 2
      config/genconfig.py
  3. 18
      doc/low-memory.rst
  4. 14
      src/builtins.yaml
  5. 304
      src/genbuiltins.py
  6. 2
      util/example_rombuild.sh
  7. 14
      util/example_user_builtins1.yaml
  8. 5
      util/make_dist.py

4
RELEASES.rst

@ -1715,6 +1715,10 @@ Planned
and constructor call argument count from 511 to 255, and maximum Ecmascript
function constant count from 262144 to 65536 (GH-903)
* Add support for converting ROM function property values into lightfuncs in
genbuiltins.py to reduce code footprint for ROM-based built-ins and custom
bindings; footprint reduction is around 14-15kB on 32-bit targets (GH-872)
* Allow ES6 Annex B unescaped right bracket (']') in regular expressions
(non-standard before ES6 Annex B), left bracket ('[') not yet supported
because it needs backtracking (GH-871)

2
config/genconfig.py

@ -460,7 +460,7 @@ def fill_dependencies_for_snippets(snippets, idx_deps):
# print(repr(graph))
# print(repr(snlist))
# print('Resolved helper defines: %r' % resolved)
print('Resolved %d helper defines' % len(resolved))
# print('Resolved %d helper defines' % len(resolved))
def serialize_snippet_list(snippets):
ret = []

18
doc/low-memory.rst

@ -33,13 +33,13 @@ realistic memory targets are roughly:
various internal structures (strings, buffers, objects), pointer
compression, external strings, etc may need to be used
* 256kB flash memory (code) and 96kB system RAM
* 192-256kB flash memory (code) and 96kB system RAM
- Requires a bare metal system, possibly a custom C library, etc.
- http://pt.slideshare.net/seoyounghwang77/js-onmicrocontrollers
* 256kB flash memory (code) and 64kB system RAM
* 160-192kB flash memory (code) and 64kB system RAM
- Requires a bare metal system, possibly a custom C library, etc.
@ -347,7 +347,9 @@ The following may be appropriate when even less memory is available
- Rerun ``make_dist.py`` with ``--rom-support`` to create a distributable
with support for ROM builtins. ROM builtin support is not enabled by
default because it increases the size of ``duktape.c`` considerably.
(See ``util/example_rombuild.sh`` for some very simple examples.)
Add the option ``--rom-auto-lightfunc`` to convert built-in function
properties into lightfuncs to reduce ROM footprint. (See
``util/example_rombuild.sh`` for some very simple examples.)
- Moving built-ins into ROM makes them read-only which has some side
effects. Some side effects are technical compliance issues while
@ -378,6 +380,16 @@ The following may be appropriate when even less memory is available
+ ``util/example_rombuild.sh``: illustrates how to run ``make_dist.py``
with user builtins
* Consider using lightfuncs for representing function properties of ROM
built-ins.
- For custom built-ins you can use a "lightfunc" type for a property
value directly.
- You can also request automatic lightfunc conversion for built-in
function properties using ``--rom-auto-lightfunc``. This saves
around 15kB for Duktape built-ins.
Notes on pointer compression
============================

14
src/builtins.yaml

@ -34,6 +34,10 @@
# + e: enumerable
# + c: configurable
# + a: accessor (determined automatically, not necessary to give explicitly)
# - autoLightfunc: if true (which is default), function property may be
# automatically converted to a lightfunc if other automatic lightfunc
# conversion criteria are met; use "autoLightfunc: false" to prevent
# lightfunc conversion
# - May also have feature tags like "es6: true" to indicate property
# is related to a specific standard
# - To disable a property without removing its metadata, you can use
@ -61,6 +65,13 @@
# - Pointer:
# - type: pointer
# - NOTE: not supported yet, type is reserved
# - Lightfunc:
# - type: lightfunc
# - native: native function name
# - magic: -128 to 127
# - length: 0 to 15
# - nargs: 0 to 14; or varargs: true
# - varargs: if true, target has variable arguments
# - Reference to a built-in object:
# - type: object
# id: ID string of target object
@ -315,6 +326,7 @@ objects:
type: function
native: duk_bi_global_object_eval
length: 1
autoLightfunc: false # automatic lightfunc conversion clashes with internal implementation
- key: "parseInt"
value:
type: function
@ -2267,12 +2279,14 @@ objects:
native: duk_bi_thread_yield
length: 2
duktape: true
autoLightfunc: false # automatic lightfunc conversion clashes with internal implementation
- key: "resume"
value:
type: function
native: duk_bi_thread_resume
length: 3
duktape: true
autoLightfunc: false # automatic lightfunc conversion clashes with internal implementation
- key: "current"
value:
type: function

304
src/genbuiltins.py

@ -21,6 +21,7 @@
import os
import sys
import re
import traceback
import json
import yaml
import math
@ -134,11 +135,23 @@ def string_is_arridx(v):
def metadata_lookup_object(meta, obj_id):
return meta['_objid_to_object'][obj_id]
def metadata_lookup_object_and_index(meta, obj_id):
for i,t in enumerate(meta['objects']):
if t['id'] == obj_id:
return t, i
return None, None
def metadata_lookup_property(obj, key):
for p in obj['properties']:
if p['key'] == key:
return p
raise Exception('cannot find property %s from object %s' % (key, obj_id))
return None
def metadata_lookup_property_and_index(obj, key):
for i,t in enumerate(obj['properties']):
if t['key'] == key:
return t, i
return None, None
# Remove disabled objects and properties.
def metadata_remove_disabled(meta):
@ -187,18 +200,6 @@ def metadata_delete_dangling_references_to_object(meta, obj_id):
# Merge a user YAML file into current metadata.
def metadata_merge_user_objects(meta, user_meta):
# XXX: could be reused from other call sites
def _findObject(objid):
for i,t in enumerate(meta['objects']):
if t['id'] == objid:
return t, i
return None, None
def _findProp(obj, key):
for i,t in enumerate(obj['properties']):
if t['key'] == key:
return t, i
return None, None
if user_meta.has_key('add_objects'):
raise Exception('"add_objects" removed, use "objects" with "add: True"')
if user_meta.has_key('replace_objects'):
@ -210,7 +211,7 @@ def metadata_merge_user_objects(meta, user_meta):
if o.get('disable', False):
print('Skip disabled object: %s' % o['id'])
continue
targ, targ_idx = _findObject(o['id'])
targ, targ_idx = metadata_lookup_object_and_index(meta, o['id'])
if o.get('delete', False):
print('Delete object: %s' % targ['id'])
@ -251,7 +252,7 @@ def metadata_merge_user_objects(meta, user_meta):
continue
prop = None
prop_idx = None
prop, prop_idx = _findProp(targ, p['key'])
prop, prop_idx = metadata_lookup_property_and_index(targ, p['key'])
if prop is not None:
if p.get('delete', False):
print('Delete property %s of %s' % (p['key'], o['id']))
@ -435,6 +436,13 @@ def metadata_normalize_shorthand(meta):
val = structprop['value']['value']
return decodeStructuredValue(val)
def clonePropShared(prop):
res = {}
for k in [ 'key', 'attributes', 'autoLightfunc' ]:
if prop.has_key(k):
res[k] = prop[k]
return res
for idx,obj in enumerate(meta['objects']):
props = []
repl_props = []
@ -449,7 +457,8 @@ def metadata_normalize_shorthand(meta):
if isinstance(val['value'], dict) and val['value']['type'] == 'function':
# Function shorthand.
subfun = decodeFunctionShorthand(val)
prop = { 'key': val['key'], 'value': { 'type': 'object', 'id': subfun['id'] }, 'attributes': val['attributes'] }
prop = clonePropShared(val)
prop['value'] = { 'type': 'object', 'id': subfun['id'] }
repl_props.append(prop)
elif isinstance(val['value'], dict) and val['value']['type'] == 'accessor' and \
(val['value'].has_key('getter') or val['value'].has_key('setter')):
@ -457,14 +466,16 @@ def metadata_normalize_shorthand(meta):
# but are differentiated by properties.
sub_getter = decodeGetterShorthand(val['key'], val)
sub_setter = decodeSetterShorthand(val['key'], val)
prop = { 'key': val['key'], 'value': { 'type': 'accessor', 'getter_id': sub_getter['id'], 'setter_id': sub_setter['id'] }, 'attributes': val['attributes'] }
prop = clonePropShared(val)
prop['value'] = { 'type': 'accessor', 'getter_id': sub_getter['id'], 'setter_id': sub_setter['id'] }
assert('a' in prop['attributes']) # If missing, weird things happen runtime
#print('Expand accessor shorthand: %r -> %r' % (val, prop))
repl_props.append(prop)
elif isinstance(val['value'], dict) and val['value']['type'] == 'structured':
# Structured shorthand.
subval = decodeStructuredShorthand(val)
prop = { 'key': val['key'], 'value': subval, 'attributes': val['attributes'] }
prop = clonePropShared(val)
prop['value'] = subval
repl_props.append(prop)
print('Decoded structured shorthand for object %s, property %s' % (obj['id'], val['key']))
elif isinstance(val['value'], dict) and val['value']['type'] == 'buffer':
@ -620,6 +631,93 @@ def metadata_normalize_missing_strings(meta, user_meta):
s['_auto_add_user'] = True
meta['strings'].append(s)
# Convert built-in function properties into lightfuncs where applicable.
def metadata_convert_lightfuncs(meta):
num_converted = 0
num_skipped = 0
for o in meta['objects']:
for p in o['properties']:
v = p['value']
ptype = None
if isinstance(v, dict):
ptype = p['value']['type']
if ptype != 'object':
continue
targ, targ_idx = metadata_lookup_object_and_index(meta, p['value']['id'])
reasons = []
if not targ.get('callable', False):
reasons.append('not-callable')
#if targ.get('constructable', False):
# reasons.append('constructable')
lf_len = 0
for p2 in targ['properties']:
# Don't convert if function has more properties than
# we're willing to sacrifice.
#print(' - Check %r . %s' % (o.get('id', None), p2['key']))
if p2['key'] == 'length' and isinstance(p2['value'], (int, long)):
lf_len = p2['value']
if p2['key'] not in [ 'length', 'name' ]:
reasons.append('nonallowed-property')
if not p.get('autoLightfunc', True):
print('Automatic lightfunc conversion rejected for key %s, explicitly requested in metadata' % p['key'])
reasons.append('no-auto-lightfunc')
# lf_len comes from actual property table (after normalization)
if targ.has_key('magic'):
try:
# Magic values which resolve to 'bidx' indices cannot
# be resolved here yet, because the bidx map is not
# yet ready. If so, reject the lightfunc conversion
# for now. In practice this doesn't matter.
lf_magic = resolve_magic(targ.get('magic'), {}) # empty map is a "fake" bidx map
#print('resolved magic ok -> %r' % lf_magic)
except Exception, e:
#print('Failed to resolve magic for %r: %r' % (p['key'], e))
reasons.append('magic-resolve-failed')
lf_magic = 0xffffffff # dummy, will be out of bounds
else:
lf_magic = 0
if targ.get('varargs', True):
lf_nargs = None
lf_varargs = True
else:
lf_nargs = targ['nargs']
lf_varargs = False
if lf_len < 0 or lf_len > 15:
#print('lf_len out of bounds: %r' % lf_len)
reasons.append('len-bounds')
if lf_magic < -0x80 or lf_magic > 0x7f:
#print('lf_magic out of bounds: %r' % lf_magic)
reasons.append('magic-bounds')
if not lf_varargs and (lf_nargs < 0 or lf_nargs > 14):
#print('lf_nargs out of bounds: %r' % lf_nargs)
reasons.append('nargs-bounds')
if len(reasons) > 0:
#print('Don\'t convert to lightfunc: %r %r (%r): %r' % (o.get('id', None), p.get('key', None), p['value']['id'], reasons))
num_skipped += 1
continue
p_id = p['value']['id']
p['value'] = {
'type': 'lightfunc',
'native': targ['native'],
'length': lf_len,
'magic': lf_magic,
'nargs': lf_nargs,
'varargs': lf_varargs
}
#print(' - Convert to lightfunc: %r %r (%r) -> %r' % (o.get('id', None), p.get('key', None), p_id, p['value']))
num_converted += 1
print('Converted %d built-in function properties to lightfuncs, %d skipped as non-eligible' % (num_converted, num_skipped))
# Detect objects not reachable from any object with a 'bidx'. This is usually
# a user error because such objects can't be reached at runtime so they're
# useless in RAM or ROM init data.
@ -659,16 +757,20 @@ def metadata_remove_orphan_objects(meta):
if reachable_count == len(reachable.keys()):
break
num_deleted = 0
deleted = True
while deleted:
deleted = False
for i,o in enumerate(meta['objects']):
if not reachable.has_key(o['id']):
print('WARNING: object %s not reachable, dropping' % o['id'])
#print('WARNING: object %s not reachable, dropping' % o['id'])
meta['objects'].pop(i)
deleted = True
num_deleted += 1
break
print('Deleted %d unreachable objects' % num_deleted)
# Add C define names for builtin strings. These defines are added to all
# strings, even when they won't get a stridx because the define names are
# used to autodetect referenced strings.
@ -883,6 +985,11 @@ def load_metadata(opts, rom=False, build_info=None):
if rom:
metadata_normalize_rom_property_attributes(meta)
# Convert built-in function properties automatically into
# lightfuncs if requested and function is eligible.
if rom and opts.rom_auto_lightfunc:
metadata_convert_lightfuncs(meta)
# Create a list of objects needing a 'bidx'. This is now just
# based on the 'builtins' metadata list but could be dynamically
# scanned somehow. Ensure 'objects' and 'objects_bidx' match
@ -1121,9 +1228,7 @@ def resolve_magic(elem, objid_to_bidx):
v = int(elem)
if not (v >= -0x8000 and v <= 0x7fff):
raise Exception('invalid plain value for magic: %s' % repr(v))
# Magic is a 16-bit signed value, but convert to 16-bit signed
# for encoding
return v & 0xffff
return v
if not isinstance(elem, dict):
raise Exception('invalid magic: %r' % elem)
@ -1136,9 +1241,7 @@ def resolve_magic(elem, objid_to_bidx):
v = elem['value']
if not (v >= -0x8000 and v <= 0x7fff):
raise Exception('invalid plain value for magic: %s' % repr(v))
# Magic is a 16-bit signed value, but convert to 16-bit signed
# for encoding
return v & 0xffff
return v
elif elem['type'] == 'math_onearg':
return math_onearg_magic[elem['funcname']]
elif elem['type'] == 'math_twoarg':
@ -1516,7 +1619,8 @@ def gen_ramobj_initdata_for_object(meta, be, bi, string_to_stridx, natfunc_name_
else:
be.bits(0, 1) # flag: not constructable
magic = resolve_magic(bi.get('magic'), objid_to_bidx)
# Convert signed magic to 16-bit unsigned for encoding
magic = resolve_magic(bi.get('magic'), objid_to_bidx) & 0xffff
if magic != 0:
assert(magic >= 0)
assert(magic < (1 << MAGIC_BITS))
@ -1708,6 +1812,9 @@ def gen_ramobj_initdata_for_props(meta, be, bi, string_to_stridx, natfunc_name_t
assert(setter_fn['nargs'] == 1)
assert(getter_fn['magic'] == 0)
assert(setter_fn['magic'] == 0)
elif val['type'] == 'lightfunc':
print('WARNING: RAM init data format doesn\'t support "lightfunc" now, value replaced with "undefined": %r' % valspec)
be.bits(PROP_TYPE_UNDEFINED, PROP_TYPE_BITS)
else:
raise Exception('unsupported value: %s' % repr(val))
else:
@ -1739,7 +1846,8 @@ def gen_ramobj_initdata_for_props(meta, be, bi, string_to_stridx, natfunc_name_t
# XXX: make this check conditional to minimize bit count
# (there are quite a lot of function properties)
magic = resolve_magic(funobj.get('magic'), objid_to_bidx)
# Convert signed magic to 16-bit unsigned for encoding
magic = resolve_magic(funobj.get('magic'), objid_to_bidx) & 0xffff
if magic != 0:
assert(magic >= 0)
assert(magic < (1 << MAGIC_BITS))
@ -1772,6 +1880,10 @@ def get_ramobj_native_func_maps(meta):
target = metadata_lookup_object(meta, val['id'])
if target.has_key('native'):
native_funcs_found[target['native']] = True
if val['type'] == 'lightfunc':
# No lightfunc support for RAM initializer now.
pass
for idx,k in enumerate(sorted(native_funcs_found.keys())):
native_funcs.append(k) # native func names
natfunc_name_to_natidx[k] = idx
@ -1933,6 +2045,9 @@ def rom_get_value_initializer(meta, val, bi_str_map, bi_obj_map):
elif v['type'] == 'undefined':
init_type = 'duk_rom_tval_undefined'
init_lit = 'DUK__TVAL_UNDEFINED()'
elif v['type'] == 'null':
init_type = 'duk_rom_tval_null'
init_lit = 'DUK__TVAL_UNDEFINED()'
elif v['type'] == 'object':
init_type = 'duk_rom_tval_object'
init_lit = 'DUK__TVAL_OBJECT(&%s)' % bi_obj_map[v['id']]
@ -1940,7 +2055,28 @@ def rom_get_value_initializer(meta, val, bi_str_map, bi_obj_map):
getter_object = metadata_lookup_object(meta, v['getter_id'])
setter_object = metadata_lookup_object(meta, v['setter_id'])
init_type = 'duk_rom_tval_accessor'
init_lit = '{ (const duk_hobject *) &%s, (const duk_hobject *) &%s }' % (bi_obj_map[getter_object['id']], bi_obj_map[setter_object['id']])
init_lit = 'DUK__TVAL_ACCESSOR(&%s, &%s)' % (bi_obj_map[getter_object['id']], bi_obj_map[setter_object['id']])
elif v['type'] == 'lightfunc':
# Match DUK_LFUNC_FLAGS_PACK() in duk_tval.h.
if v.has_key('length'):
assert(v['length'] >= 0 and v['length'] <= 15)
lf_length = v['length']
else:
lf_length = 0
if v.get('varargs', True):
lf_nargs = 15 # varargs marker
else:
assert(v['nargs'] >= 0 and v['nargs'] <= 14)
lf_nargs = v['nargs']
if v.has_key('magic'):
assert(v['magic'] >= -0x80 and v['magic'] <= 0x7f)
lf_magic = v['magic'] & 0xff
else:
lf_magic = 0
lf_flags = (lf_magic << 8) + (lf_length << 4) + lf_nargs
init_type = 'duk_rom_tval_lightfunc'
init_lit = 'DUK__TVAL_LIGHTFUNC(%s, %dL)' % (v['native'], lf_flags)
else:
raise Exception('unhandled value: %r' % val)
else:
@ -2152,11 +2288,17 @@ def rom_emit_object_initializer_types_and_macros(genc):
genc.emitLine('\t{ { { { (heaphdr_flags), (refcount), NULL, NULL }, (duk_uint8_t *) DUK_LOSE_CONST(props), (duk_hobject *) DUK_LOSE_CONST(iproto), (esize), (enext), (asize), (hsize) }, (nativefunc), (duk_int16_t) (nargs), (duk_int16_t) (magic) } }')
genc.emitLine('#endif /* DUK_USE_HEAPPTR16 */')
# Initializer typedef for a dummy function pointer. ROM support assumes
# function pointers are 32 bits. Using a dummy function pointer type
# avoids function pointer to normal pointer cast which emits warnings.
genc.emitLine('typedef void (*duk_rom_funcptr)(void);')
# Emit duk_tval structs. This gets a bit messier with packed/unpacked
# duk_tval, endianness variants, pointer sizes, etc.
genc.emitLine('#if defined(DUK_USE_PACKED_TVAL)')
genc.emitLine('typedef struct duk_rom_tval_undefined duk_rom_tval_undefined;')
genc.emitLine('typedef struct duk_rom_tval_null duk_rom_tval_null;')
genc.emitLine('typedef struct duk_rom_tval_lightfunc duk_rom_tval_lightfunc;')
genc.emitLine('typedef struct duk_rom_tval_boolean duk_rom_tval_boolean;')
genc.emitLine('typedef struct duk_rom_tval_number duk_rom_tval_number;')
genc.emitLine('typedef struct duk_rom_tval_object duk_rom_tval_object;')
@ -2169,18 +2311,21 @@ def rom_emit_object_initializer_types_and_macros(genc):
genc.emitLine('struct duk_rom_tval_string { const void *ptr; duk_uint32_t hiword; };')
genc.emitLine('struct duk_rom_tval_undefined { const void *ptr; duk_uint32_t hiword; };')
genc.emitLine('struct duk_rom_tval_null { const void *ptr; duk_uint32_t hiword; };')
genc.emitLine('struct duk_rom_tval_lightfunc { duk_rom_funcptr ptr; duk_uint32_t hiword; };')
genc.emitLine('struct duk_rom_tval_boolean { duk_uint32_t dummy; duk_uint32_t hiword; };')
genc.emitLine('#elif defined(DUK_USE_DOUBLE_BE)')
genc.emitLine('struct duk_rom_tval_object { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_string { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_undefined { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_null { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_lightfunc { duk_uint32_t hiword; duk_rom_funcptr ptr; };')
genc.emitLine('struct duk_rom_tval_boolean { duk_uint32_t hiword; duk_uint32_t dummy; };')
genc.emitLine('#elif defined(DUK_USE_DOUBLE_ME)')
genc.emitLine('struct duk_rom_tval_object { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_string { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_undefined { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_null { duk_uint32_t hiword; const void *ptr; };')
genc.emitLine('struct duk_rom_tval_lightfunc { duk_uint32_t hiword; duk_rom_funcptr ptr; };')
genc.emitLine('struct duk_rom_tval_boolean { duk_uint32_t hiword; duk_uint32_t dummy; };')
genc.emitLine('#else')
genc.emitLine('#error invalid endianness defines')
@ -2208,6 +2353,8 @@ def rom_emit_object_initializer_types_and_macros(genc):
genc.emitLine('struct duk_rom_tval_object { duk_small_uint_t tag; duk_small_uint_t extra; const duk_heaphdr *val; };')
genc.emitLine('typedef struct duk_rom_tval_string duk_rom_tval_string;')
genc.emitLine('struct duk_rom_tval_string { duk_small_uint_t tag; duk_small_uint_t extra; const duk_heaphdr *val; };')
genc.emitLine('typedef struct duk_rom_tval_lightfunc duk_rom_tval_lightfunc;')
genc.emitLine('struct duk_rom_tval_lightfunc { duk_small_uint_t tag; duk_small_uint_t extra; duk_rom_funcptr ptr; };')
genc.emitLine('typedef struct duk_rom_tval_accessor duk_rom_tval_accessor;')
genc.emitLine('struct duk_rom_tval_accessor { const duk_hobject *get; const duk_hobject *set; };')
genc.emitLine('#endif /* DUK_USE_PACKED_TVAL */')
@ -2232,18 +2379,21 @@ def rom_emit_object_initializer_types_and_macros(genc):
genc.emitLine('#if defined(DUK_USE_DOUBLE_LE)')
genc.emitLine('#define DUK__TVAL_UNDEFINED() { (const void *) NULL, (DUK_TAG_UNDEFINED << 16) }')
genc.emitLine('#define DUK__TVAL_NULL() { (const void *) NULL, (DUK_TAG_NULL << 16) }')
genc.emitLine('#define DUK__TVAL_LIGHTFUNC(func,flags) { (duk_rom_funcptr) (func), (DUK_TAG_LIGHTFUNC << 16) + (flags) }')
genc.emitLine('#define DUK__TVAL_BOOLEAN(bval) { 0, (DUK_TAG_BOOLEAN << 16) + (bval) }')
genc.emitLine('#define DUK__TVAL_OBJECT(ptr) { (const void *) (ptr), (DUK_TAG_OBJECT << 16) }')
genc.emitLine('#define DUK__TVAL_STRING(ptr) { (const void *) (ptr), (DUK_TAG_STRING << 16) }')
genc.emitLine('#elif defined(DUK_USE_DOUBLE_BE)')
genc.emitLine('#define DUK__TVAL_UNDEFINED() { (DUK_TAG_UNDEFINED << 16), (const void *) NULL }')
genc.emitLine('#define DUK__TVAL_NULL() { (DUK_TAG_NULL << 16), (const void *) NULL }')
genc.emitLine('#define DUK__TVAL_LIGHTFUNC(func,flags) { (DUK_TAG_LIGHTFUNC << 16) + (flags), (duk_rom_funcptr) (func) }')
genc.emitLine('#define DUK__TVAL_BOOLEAN(bval) { (DUK_TAG_BOOLEAN << 16) + (bval), 0 }')
genc.emitLine('#define DUK__TVAL_OBJECT(ptr) { (DUK_TAG_OBJECT << 16), (const void *) (ptr) }')
genc.emitLine('#define DUK__TVAL_STRING(ptr) { (DUK_TAG_STRING << 16), (const void *) (ptr) }')
genc.emitLine('#elif defined(DUK_USE_DOUBLE_ME)')
genc.emitLine('#define DUK__TVAL_UNDEFINED() { (DUK_TAG_UNDEFINED << 16), (const void *) NULL }')
genc.emitLine('#define DUK__TVAL_NULL() { (DUK_TAG_NULL << 16), (const void *) NULL }')
genc.emitLine('#define DUK__TVAL_LIGHTFUNC(func,flags) { (DUK_TAG_LIGHTFUNC << 16) + (flags), (duk_rom_funcptr) (func) }')
genc.emitLine('#define DUK__TVAL_BOOLEAN(bval) { (DUK_TAG_BOOLEAN << 16) + (bval), 0 }')
genc.emitLine('#define DUK__TVAL_OBJECT(ptr) { (DUK_TAG_OBJECT << 16), (const void *) (ptr) }')
genc.emitLine('#define DUK__TVAL_STRING(ptr) { (DUK_TAG_STRING << 16), (const void *) (ptr) }')
@ -2257,7 +2407,9 @@ def rom_emit_object_initializer_types_and_macros(genc):
genc.emitLine('#define DUK__TVAL_BOOLEAN(bval) { DUK_TAG_BOOLEAN, 0, (bval), 0 }')
genc.emitLine('#define DUK__TVAL_OBJECT(ptr) { DUK_TAG_OBJECT, 0, (const duk_heaphdr *) (ptr) }')
genc.emitLine('#define DUK__TVAL_STRING(ptr) { DUK_TAG_STRING, 0, (const duk_heaphdr *) (ptr) }')
genc.emitLine('#define DUK__TVAL_LIGHTFUNC(func,flags) { DUK_TAG_LIGHTFUNC, (flags), (duk_rom_funcptr) (func) }')
genc.emitLine('#endif /* DUK_USE_PACKED_TVAL */')
genc.emitLine('#define DUK__TVAL_ACCESSOR(getter,setter) { (const duk_hobject *) (getter), (const duk_hobject *) (setter) }')
# Emit ROM objects source: the object/function headers themselves, property
# table structs for different property table sizes/types, and property table
@ -2628,20 +2780,30 @@ def rom_emit_objects_header(genc, meta):
def emit_header_native_function_declarations(genc, meta):
emitted = {} # To suppress duplicates
funclist = []
def _emit(fname):
if not emitted.has_key(fname):
emitted[fname] = True
funclist.append(fname)
for o in meta['objects']:
if not o.has_key('native'):
continue
fname = o['native']
if emitted.has_key(fname):
continue # already emitted, suppress duplicate
emitted[fname] = True
if o.has_key('native'):
_emit(o['native'])
for p in o['properties']:
v = p['value']
if isinstance(v, dict) and v['type'] == 'lightfunc':
assert(v.has_key('native'))
_emit(v['native'])
#print('Lightfunc function declaration: %r' % v['native'])
for fname in funclist:
# Visibility depends on whether the function is Duktape internal or user.
# Use a simple prefix for now.
if fname[:4] == 'duk_':
genc.emitLine('DUK_INTERNAL_DECL duk_ret_t %s(duk_context *ctx);' % o['native'])
genc.emitLine('DUK_INTERNAL_DECL duk_ret_t %s(duk_context *ctx);' % fname)
else:
genc.emitLine('extern duk_ret_t %s(duk_context *ctx);' % o['native'])
genc.emitLine('extern duk_ret_t %s(duk_context *ctx);' % fname)
#
# Main
@ -2654,7 +2816,9 @@ def main():
parser.add_option('--strings-metadata', dest='strings_metadata', help='Built-in strings metadata file, YAML format')
parser.add_option('--objects-metadata', dest='objects_metadata', help='Built-in objects metadata file, YAML format')
parser.add_option('--user-builtin-metadata', dest='user_builtin_metadata', action='append', default=[], help='User strings and objects to add, YAML format (can be repeated for multiple overrides)')
parser.add_option('--ram-support', dest='ram_support', action='store_true', default=False, help='Support RAM strings/objects')
parser.add_option('--rom-support', dest='rom_support', action='store_true', default=False, help='Support ROM strings/objects (increases output size considerably)')
parser.add_option('--rom-auto-lightfunc', dest='rom_auto_lightfunc', action='store_true', default=False, help='Convert ROM built-in function properties into lightfuncs automatically whenever possible')
parser.add_option('--out-header', dest='out_header', help='Output header file')
parser.add_option('--out-source', dest='out_source', help='Output source file')
parser.add_option('--out-metadata-json', dest='out_metadata_json', help='Output metadata file')
@ -2684,9 +2848,10 @@ def main():
ramstr_data, ramstr_maxlen = gen_ramstr_initdata_bitpacked(ram_meta)
ram_native_funcs, ram_natfunc_name_to_natidx = get_ramobj_native_func_maps(ram_meta)
ramobj_data_le = gen_ramobj_initdata_bitpacked(ram_meta, ram_native_funcs, ram_natfunc_name_to_natidx, 'little')
ramobj_data_be = gen_ramobj_initdata_bitpacked(ram_meta, ram_native_funcs, ram_natfunc_name_to_natidx, 'big')
ramobj_data_me = gen_ramobj_initdata_bitpacked(ram_meta, ram_native_funcs, ram_natfunc_name_to_natidx, 'mixed')
if opts.ram_support:
ramobj_data_le = gen_ramobj_initdata_bitpacked(ram_meta, ram_native_funcs, ram_natfunc_name_to_natidx, 'little')
ramobj_data_be = gen_ramobj_initdata_bitpacked(ram_meta, ram_native_funcs, ram_natfunc_name_to_natidx, 'big')
ramobj_data_me = gen_ramobj_initdata_bitpacked(ram_meta, ram_native_funcs, ram_natfunc_name_to_natidx, 'mixed')
# Write source and header files.
@ -2713,16 +2878,19 @@ def main():
else:
gc_src.emitLine('#error ROM support not enabled, rerun make_dist.py with --rom-support')
gc_src.emitLine('#else /* DUK_USE_ROM_OBJECTS */')
emit_ramobj_source_nativefunc_array(gc_src, ram_native_funcs) # endian independent
gc_src.emitLine('#if defined(DUK_USE_DOUBLE_LE)')
emit_ramobj_source_objinit_data(gc_src, ramobj_data_le)
gc_src.emitLine('#elif defined(DUK_USE_DOUBLE_BE)')
emit_ramobj_source_objinit_data(gc_src, ramobj_data_be)
gc_src.emitLine('#elif defined(DUK_USE_DOUBLE_ME)')
emit_ramobj_source_objinit_data(gc_src, ramobj_data_me)
gc_src.emitLine('#else')
gc_src.emitLine('#error invalid endianness defines')
gc_src.emitLine('#endif')
if opts.ram_support:
emit_ramobj_source_nativefunc_array(gc_src, ram_native_funcs) # endian independent
gc_src.emitLine('#if defined(DUK_USE_DOUBLE_LE)')
emit_ramobj_source_objinit_data(gc_src, ramobj_data_le)
gc_src.emitLine('#elif defined(DUK_USE_DOUBLE_BE)')
emit_ramobj_source_objinit_data(gc_src, ramobj_data_be)
gc_src.emitLine('#elif defined(DUK_USE_DOUBLE_ME)')
emit_ramobj_source_objinit_data(gc_src, ramobj_data_me)
gc_src.emitLine('#else')
gc_src.emitLine('#error invalid endianness defines')
gc_src.emitLine('#endif')
else:
gc_src.emitLine('#error RAM support not enabled, rerun make_dist.py with --ram-support')
gc_src.emitLine('#endif /* DUK_USE_ROM_OBJECTS */')
gc_hdr = dukutil.GenerateC()
@ -2737,8 +2905,11 @@ def main():
else:
gc_hdr.emitLine('#error ROM support not enabled, rerun make_dist.py with --rom-support')
gc_hdr.emitLine('#else /* DUK_USE_ROM_STRINGS */')
emit_header_stridx_defines(gc_hdr, ram_meta)
emit_ramstr_header_strinit_defines(gc_hdr, ram_meta, ramstr_data, ramstr_maxlen)
if opts.ram_support:
emit_header_stridx_defines(gc_hdr, ram_meta)
emit_ramstr_header_strinit_defines(gc_hdr, ram_meta, ramstr_data, ramstr_maxlen)
else:
gc_hdr.emitLine('#error RAM support not enabled, rerun make_dist.py with --ram-support')
gc_hdr.emitLine('#endif /* DUK_USE_ROM_STRINGS */')
gc_hdr.emitLine('')
gc_hdr.emitLine('#if defined(DUK_USE_ROM_OBJECTS)')
@ -2755,20 +2926,23 @@ def main():
emit_header_native_function_declarations(gc_hdr, rom_meta)
rom_emit_objects_header(gc_hdr, rom_meta)
else:
gc_hdr.emitLine('#error ROM support not enabled, rerun make_dist.py with --rom-support')
gc_hdr.emitLine('#else')
emit_header_native_function_declarations(gc_hdr, rom_meta)
emit_ramobj_header_nativefunc_array(gc_hdr, ram_native_funcs)
emit_ramobj_header_objects(gc_hdr, ram_meta)
gc_hdr.emitLine('#if defined(DUK_USE_DOUBLE_LE)')
emit_ramobj_header_initdata(gc_hdr, ramobj_data_le)
gc_hdr.emitLine('#elif defined(DUK_USE_DOUBLE_BE)')
emit_ramobj_header_initdata(gc_hdr, ramobj_data_be)
gc_hdr.emitLine('#elif defined(DUK_USE_DOUBLE_ME)')
emit_ramobj_header_initdata(gc_hdr, ramobj_data_me)
gc_hdr.emitLine('#else')
gc_hdr.emitLine('#error invalid endianness defines')
gc_hdr.emitLine('#endif')
gc_hdr.emitLine('#error RAM support not enabled, rerun make_dist.py with --ram-support')
gc_hdr.emitLine('#else /* DUK_USE_ROM_OBJECTS */')
if opts.ram_support:
emit_header_native_function_declarations(gc_hdr, ram_meta)
emit_ramobj_header_nativefunc_array(gc_hdr, ram_native_funcs)
emit_ramobj_header_objects(gc_hdr, ram_meta)
gc_hdr.emitLine('#if defined(DUK_USE_DOUBLE_LE)')
emit_ramobj_header_initdata(gc_hdr, ramobj_data_le)
gc_hdr.emitLine('#elif defined(DUK_USE_DOUBLE_BE)')
emit_ramobj_header_initdata(gc_hdr, ramobj_data_be)
gc_hdr.emitLine('#elif defined(DUK_USE_DOUBLE_ME)')
emit_ramobj_header_initdata(gc_hdr, ramobj_data_me)
gc_hdr.emitLine('#else')
gc_hdr.emitLine('#error invalid endianness defines')
gc_hdr.emitLine('#endif')
else:
gc_hdr.emitLine('#error RAM support not enabled, rerun make_dist.py with --ram-support')
gc_hdr.emitLine('#endif /* DUK_USE_ROM_OBJECTS */')
gc_hdr.emitLine('#endif /* DUK_BUILTINS_H_INCLUDED */')

2
util/example_rombuild.sh

@ -12,6 +12,7 @@ PYTHON=`which python2 python | head -1`
make clean
$PYTHON util/make_dist.py \
--rom-support \
--rom-auto-lightfunc \
--user-builtin-metadata util/example_user_builtins1.yaml \
--user-builtin-metadata util/example_user_builtins2.yaml
@ -37,6 +38,7 @@ make duk dukd # XXX: currently fails to start, DUK_CMDLINE_LOGGING_SUPPORT, DUK
$PYTHON config/genconfig.py \
--metadata config \
--output dist/src/duk_config.h \
--option-file config/examples/low_memory.yaml \
-DDUK_USE_ROM_STRINGS \
-DDUK_USE_ROM_OBJECTS \
-DDUK_USE_ROM_GLOBAL_INHERIT \

14
util/example_user_builtins1.yaml

@ -139,8 +139,8 @@ objects:
# Example of plain value types supported at the moment:
#
# - Object, function, and accessor types not illustrated here
# (see src/builtins.yaml for examples of those).
# - Object, function, lightfunc, and accessor types not illustrated
# here (see src/builtins.yaml for examples of those).
#
# - Duktape buffer and pointer types cannot be used by built-in
# objects at the moment.
@ -187,6 +187,16 @@ objects:
value: "foobar" # Encode string as UTF-8, then map bytes to U+0000...U+00FF
attributes: wec
#- key: "lightfuncType"
# value:
# type: lightfunc
# native: duk_bi_math_object_random
# nargs: 0
# varargs: false
# length: 13
# magic: 0
# attributes: wec
# Object IDs are only resolved when metadata loading is complete, so it's
# OK to create reference loops or refer to objects defined later (even in
# a separate YAML file not yet loaded).

5
util/make_dist.py

@ -202,6 +202,7 @@ parser.add_option('--git-commit', dest='git_commit', default=None, help='Force g
parser.add_option('--git-describe', dest='git_describe', default=None, help='Force git describe')
parser.add_option('--git-branch', dest='git_branch', default=None, help='Force git branch name')
parser.add_option('--rom-support', dest='rom_support', action='store_true', help='Add support for ROM strings/objects (increases duktape.c size considerably)')
parser.add_option('--rom-auto-lightfunc', dest='rom_auto_lightfunc', action='store_true', default=False, help='Convert ROM built-in function properties into lightfuncs automatically whenever possible')
parser.add_option('--user-builtin-metadata', dest='user_builtin_metadata', action='append', default=[], help='User strings and objects to add, YAML format (can be repeated for multiple overrides)')
(opts, args) = parser.parse_args()
@ -814,11 +815,15 @@ with open(os.path.join(dist, 'duk_used_stridx_bidx_defs.json.tmp'), 'wb') as f:
f.write(res)
gb_opts = []
gb_opts.append('--ram-support') # enable by default
if opts.rom_support:
# ROM string/object support is not enabled by default because
# it increases the generated duktape.c considerably.
print('Enabling --rom-support for genbuiltins.py')
gb_opts.append('--rom-support')
if opts.rom_auto_lightfunc:
print('Enabling --rom-auto-lightfunc for genbuiltins.py')
gb_opts.append('--rom-auto-lightfunc')
for fn in opts.user_builtin_metadata:
print('Forwarding --user-builtin-metadata %s' % fn)
gb_opts.append('--user-builtin-metadata')

Loading…
Cancel
Save